e3bda32000-01-12Martin Nilsson // This is a roxen protocol module.
199d031999-09-05Francesco Chemolli // Modified by Francesco Chemolli to add throttling capabilities.
b643282000-03-16Martin Nilsson // Copyright © 1996 - 2000, Roxen IS.
6a409d1999-12-27Martin Nilsson 
8fb48a2000-06-26David Hedbor constant cvs_version = "$Id: http.pike,v 1.233 2000/06/26 21:04:12 neotron Exp $";
2a2a5b1996-12-01Per Hedbor 
86e77d1998-05-07Per Hedbor #define MAGIC_ERROR #ifdef MAGIC_ERROR inherit "highlight_pike"; #endif
6a409d1999-12-27Martin Nilsson 
2a2a5b1996-12-01Per Hedbor // HTTP protocol module.
b1fca01996-11-12Per Hedbor #include <config.h>
c5e0961999-10-04Per Hedbor 
ebb1c51998-02-24Per Hedbor #ifdef PROFILE
c5e0961999-10-04Per Hedbor #define HRTIME() gethrtime() #define HRSEC(X) ((int)((X)*1000000)) #define SECHR(X) ((X)/(float)1000000)
8afc811998-02-04Per Hedbor int req_time = HRTIME();
ebb1c51998-02-24Per Hedbor #endif
c5e0961999-10-04Per Hedbor 
cbe6c62000-03-18Martin Stjernholm #ifdef ID_OBJ_DEBUG Debug.ObjectMarker __marker = Debug.ObjectMarker (this_object()); #endif
aa92c11998-08-20Henrik Grubbström (Grubba) #ifdef REQUEST_DEBUG
41b77c1999-07-15David Hedbor int footime, bartime;
338ffa2000-02-15Martin Nilsson #define REQUEST_WERR(X) bartime = gethrtime()-footime; werror("%s (%d)\n", (X), bartime);footime=gethrtime()
aa92c11998-08-20Henrik Grubbström (Grubba) #else
6a409d1999-12-27Martin Nilsson #define REQUEST_WERR(X)
aa92c11998-08-20Henrik Grubbström (Grubba) #endif
3235691998-03-26Per Hedbor #ifdef FD_DEBUG
6a409d1999-12-27Martin Nilsson #define MARK_FD(X) catch{REQUEST_WERR(X); mark_fd(my_fd->query_fd(), (X)+" "+remoteaddr);}
3235691998-03-26Per Hedbor #else
6a409d1999-12-27Martin Nilsson #define MARK_FD(X) REQUEST_WERR(X)
3235691998-03-26Per Hedbor #endif
bdd3082000-05-14Francesco Chemolli #ifdef THROTTLING_DEBUG #undef THROTTLING_DEBUG #define THROTTLING_DEBUG(X) werror("Throttling: "+X+"\n") #else #define THROTTLING_DEBUG(X) #endif
f49bd32000-01-20Martin Nilsson constant decode = MIME.decode_base64;
338ffa2000-02-15Martin Nilsson constant find_supports_and_vars = roxen.find_supports_and_vars;
f49bd32000-01-20Martin Nilsson constant version = roxen.version; constant _query = roxen.query; constant _time = predef::time;
b1fca01996-11-12Per Hedbor 
7ee5651996-12-10Per Hedbor private static array(string) cache; private static int wanted_data, have_data;
b1fca01996-11-12Per Hedbor object conf;
ddefa61999-10-04Marcus Comstedt object port_obj;
b1fca01996-11-12Per Hedbor  #include <roxen.h> #include <module.h>
470ed72000-01-03Martin Nilsson #include <variables.h>
b1fca01996-11-12Per Hedbor 
9211b21998-04-24Per Hedbor int time;
b1fca01996-11-12Per Hedbor string raw_url;
d7b0871997-08-31Per Hedbor int do_not_disconnect;
8200cd1998-07-14Henrik Grubbström (Grubba) mapping (string:string) variables = ([ ]);
59f65b2000-01-05Henrik Grubbström (Grubba) mapping (string:mixed) misc = ([ #ifdef REQUEST_DEBUG "trace_enter":lambda(mixed ...args) {
52e0822000-03-07Henrik Grubbström (Grubba)  REQUEST_WERR(sprintf("TRACE_ENTER(%{%O,%})", args));
59f65b2000-01-05Henrik Grubbström (Grubba)  }, "trace_leave":lambda(mixed ...args) {
52e0822000-03-07Henrik Grubbström (Grubba)  REQUEST_WERR(sprintf("TRACE_LEAVE(%{%O,%})", args));
59f65b2000-01-05Henrik Grubbström (Grubba)  }
338ffa2000-02-15Martin Nilsson #endif // REQUEST_DEBUG
59f65b2000-01-05Henrik Grubbström (Grubba) ]);
8200cd1998-07-14Henrik Grubbström (Grubba) mapping (string:string) cookies = ([ ]); mapping (string:string) request_headers = ([ ]);
f49bd32000-01-20Martin Nilsson mapping (string:string) client_var = ([ ]);
b1fca01996-11-12Per Hedbor 
06d5331998-04-03Per Hedbor multiset (string) prestate = (< >); multiset (string) config = (< >); multiset (string) supports = (< >);
ebb1c51998-02-24Per Hedbor multiset (string) pragma = (< >);
b1fca01996-11-12Per Hedbor  string remoteaddr, host;
ebb1c51998-02-24Per Hedbor array (string) client; array (string) referer;
b1fca01996-11-12Per Hedbor 
beaca01998-02-20Per Hedbor mapping file;
b1fca01996-11-12Per Hedbor  object my_fd; /* The client. */ string prot;
1afe371997-06-12Henrik Grubbström (Grubba) string clientprot;
b1fca01996-11-12Per Hedbor string method; string realfile, virtfile; string rest_query=""; string raw; string query; string not_query;
60a1a11998-08-03Henrik Grubbström (Grubba) string extra_extension = ""; // special hack for the language module
ebb1c51998-02-24Per Hedbor string data, leftovers;
b1fca01996-11-12Per Hedbor array (int|string) auth; string rawauth, realauth; string since;
0b0d082000-02-14Per Hedbor array(string) output_charset = ({});
653d7d2000-02-14Per Hedbor string input_charset;
0b0d082000-02-14Per Hedbor 
cf68f12000-02-16Per Hedbor void set_output_charset( string|function to, int|void mode )
0b0d082000-02-14Per Hedbor {
cf68f12000-02-16Per Hedbor  if( search( output_charset, to ) != -1 ) // Already done.
653d7d2000-02-14Per Hedbor  return;
cf68f12000-02-16Per Hedbor  switch( mode ) { case 0: // Really set. output_charset = ({ to }); break; case 1: // Only set if not already set. if( !sizeof( output_charset ) ) output_charset = ({ to }); break; case 2: // Join. output_charset |= ({ to }); break; }
0b0d082000-02-14Per Hedbor }
cf68f12000-02-16Per Hedbor string charset_name( function|string what )
0b0d082000-02-14Per Hedbor {
cf68f12000-02-16Per Hedbor  switch( f )
0b0d082000-02-14Per Hedbor  {
cf68f12000-02-16Per Hedbor  case string_to_unicode: return "ISO10646-1"; case string_to_utf8: return "UTF-8"; default: return upper_case((string)what);
0b0d082000-02-14Per Hedbor  }
cf68f12000-02-16Per Hedbor } function charset_function( function|string what ) { switch( f )
0b0d082000-02-14Per Hedbor  {
cf68f12000-02-16Per Hedbor  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:
7dba2f2000-05-08Martin Nilsson  return Roxen._charset_decoder( Locale.Charset.encoder( (string)what ) )->decode;
cf68f12000-02-16Per Hedbor  } } static array(string) join_charset( string old, function|string add, function|void oldcodec) { switch( old&&upper_case(old) ) { case 0: return ({ charset_name( add ), charset_function( add ) }); 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 ) }); } } static array(string) output_encode( string what ) { string charset; function encoder; foreach( output_charset, string|function f ) [charset,encoder] = join_charset( charset, f, encoder ); if( !encoder )
62caea2000-02-14Per Hedbor  if( String.width( what ) > 8 )
0b0d082000-02-14Per Hedbor  { charset = "UTF-8";
cf68f12000-02-16Per Hedbor  encoder = string_to_utf8;
0b0d082000-02-14Per Hedbor  }
cf68f12000-02-16Per Hedbor  if( encoder ) what = encoder( what );
0b0d082000-02-14Per Hedbor  return ({ charset, what }); }
b1fca01996-11-12Per Hedbor 
ed15161999-11-19Per Hedbor void decode_map( mapping what, function decoder ) { foreach( indices( what ), mixed q ) { string ni; mixed val; if( stringp( q ) )
31c2ac2000-01-12Henrik Grubbström (Grubba)  catch { ni = decoder( q ); };
ed15161999-11-19Per Hedbor  val = what[q]; if( stringp( val ) )
31c2ac2000-01-12Henrik Grubbström (Grubba)  catch { val = decoder( val ); };
ed15161999-11-19Per Hedbor  else if( arrayp( val ) ) val = map( val, lambda( mixed q ) { if( stringp( q ) )
31c2ac2000-01-12Henrik Grubbström (Grubba)  catch { return decoder( q ); };
ed15161999-11-19Per Hedbor  return q; } ); else if( mappingp( val ) ) decode_map( val, decoder ); else if( multisetp( val ) ) val = mkmultiset( map( indices(val), lambda( mixed q ) { if( stringp( q ) )
31c2ac2000-01-12Henrik Grubbström (Grubba)  catch { return decoder( q ); };
ed15161999-11-19Per Hedbor  return q; } )); what[ni] = val; if( q != ni ) m_delete( what, q ); } }
653d7d2000-02-14Per Hedbor void decode_charset_encoding( string|function(string:string) decoder )
ed15161999-11-19Per Hedbor {
653d7d2000-02-14Per Hedbor  if(stringp(decoder))
7dba2f2000-05-08Martin Nilsson  decoder = Roxen._charset_decoder( Locale.Charset.decoder(decoder) )->decode;
653d7d2000-02-14Per Hedbor 
0b0d082000-02-14Per Hedbor  if( misc->request_charset_decoded ) return;
653d7d2000-02-14Per Hedbor 
ed15161999-11-19Per Hedbor  if( !decoder ) return;
31c2ac2000-01-12Henrik Grubbström (Grubba) 
653d7d2000-02-14Per Hedbor  misc->request_charset_decoded = 1;
31c2ac2000-01-12Henrik Grubbström (Grubba)  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 ); if( not_query ) not_query = safe_decoder( not_query );
ed15161999-11-19Per Hedbor  if( auth ) { auth = map( auth, lambda( mixed q ) { if( stringp( q ) )
31c2ac2000-01-12Henrik Grubbström (Grubba)  return safe_decoder( q );
ed15161999-11-19Per Hedbor  return q; } );
31c2ac2000-01-12Henrik Grubbström (Grubba)  rawauth = safe_decoder( rawauth ); realauth = safe_decoder( realauth );
ed15161999-11-19Per Hedbor  }
abdff21999-12-27Martin Nilsson  if( since )
31c2ac2000-01-12Henrik Grubbström (Grubba)  since = safe_decoder( since );
ed15161999-11-19Per Hedbor  decode_map( variables, decoder ); decode_map( misc, decoder ); decode_map( cookies, decoder ); decode_map( request_headers, decoder );
31c2ac2000-01-12Henrik Grubbström (Grubba) 
ed15161999-11-19Per Hedbor  if( client )
31c2ac2000-01-12Henrik Grubbström (Grubba)  client = map( client, safe_decoder );
ed15161999-11-19Per Hedbor  if( referer )
31c2ac2000-01-12Henrik Grubbström (Grubba)  referer = map( referer, safe_decoder ); prestate = mkmultiset( map( (array(string))indices( prestate ), safe_decoder ) ); config = mkmultiset( map( (array(string))indices( config ), safe_decoder ) ); pragma = mkmultiset( map( (array(string))indices( pragma ), safe_decoder ) );
ed15161999-11-19Per Hedbor }
b1fca01996-11-12Per Hedbor // Parse a HTTP/1.1 HTTP/1.0 or 0.9 request, including form data and // state variables. Return 0 if more is expected, 1 if done, and -1 // if fatal error.
ebb1c51998-02-24Per Hedbor void end(string|void a,int|void b);
b1fca01996-11-12Per Hedbor 
199d031999-09-05Francesco Chemolli object pipe;
9d8ea11999-04-16Henrik Grubbström (Grubba) 
ba88df1999-09-29Francesco Chemolli //used values: throttle->doit=0|1 to enable it // throttle->rate the rate // throttle->fixed if it's not to be touched again mapping throttle=([]);
199d031999-09-05Francesco Chemolli object throttler;//the inter-request throttling object.
9d8ea11999-04-16Henrik Grubbström (Grubba) 
199d031999-09-05Francesco Chemolli /* Pipe-using send functions */
9d8ea11999-04-16Henrik Grubbström (Grubba) 
199d031999-09-05Francesco Chemolli // FIXME: //I'm choosing the pipe type upon setup. Thus I'm assuming that all headers //have been defined before then. This is actually not true in case //of throttling and keep-alive. We'll take care of that later.
e5bad21998-02-10Per Hedbor private void setup_pipe()
b1fca01996-11-12Per Hedbor {
199d031999-09-05Francesco Chemolli  if(!my_fd) {
1842611997-05-30Henrik Grubbström (Grubba)  end(); return; }
199d031999-09-05Francesco Chemolli  if (!conf || !conf->query("req_throttle"))
ba88df1999-09-29Francesco Chemolli  throttle->doit=0;
199d031999-09-05Francesco Chemolli  if(!pipe) {
ba88df1999-09-29Francesco Chemolli  if (throttle->doit || (conf && conf->throttler)) {
a577791999-11-24Per Hedbor  pipe=roxen->slowpipe();
199d031999-09-05Francesco Chemolli  } else {
a577791999-11-24Per Hedbor  pipe=roxen->fastpipe();
199d031999-09-05Francesco Chemolli  } }
bdd3082000-05-14Francesco Chemolli  if (throttle->doit) { //we are sure that pipe is really a slowpipe.
ba88df1999-09-29Francesco Chemolli  throttle->rate=max(throttle->rate, conf->query("req_throttle_min")); //if conf=0 => throttle=0 pipe->throttle(throttle->rate, (int)(throttle->rate*conf->query("req_throttle_depth_mult")), 0);
bdd3082000-05-14Francesco Chemolli  THROTTLING_DEBUG("throtting request at "+throttle->rate);
199d031999-09-05Francesco Chemolli  }
abdff21999-12-27Martin Nilsson  if (conf && conf->throttler) {
199d031999-09-05Francesco Chemolli  pipe->assign_throttler(conf->throttler); }
b1fca01996-11-12Per Hedbor }
199d031999-09-05Francesco Chemolli  void send (string|object what, int|void len)
b1fca01996-11-12Per Hedbor {
6a409d1999-12-27Martin Nilsson  REQUEST_WERR(sprintf("send(%O, %O)\n", what, len));
aa92c11998-08-20Henrik Grubbström (Grubba) 
b1fca01996-11-12Per Hedbor  if(!what) return;
e5bad21998-02-10Per Hedbor  if(!pipe) setup_pipe(); if(!pipe) return;
b1fca01996-11-12Per Hedbor  if(stringp(what)) pipe->write(what);
e5bad21998-02-10Per Hedbor  else pipe->input(what,len);
b1fca01996-11-12Per Hedbor }
199d031999-09-05Francesco Chemolli void start_sender (function callback, mixed ... args)
9d8ea11999-04-16Henrik Grubbström (Grubba) { if (pipe) { MARK_FD("HTTP really handled, piping "+not_query); #ifdef FD_DEBUG call_out(timer, 30, _time(1)); // Update FD with time... #endif
336ee41999-04-17Henrik Grubbström (Grubba)  // FIXME: What about args? pipe->set_done_callback( callback );
9d8ea11999-04-16Henrik Grubbström (Grubba)  pipe->output(my_fd); } else { MARK_FD("HTTP really handled, pipe done");
336ee41999-04-17Henrik Grubbström (Grubba)  callback(@args);
9d8ea11999-04-16Henrik Grubbström (Grubba)  } }
b1fca01996-11-12Per Hedbor string scan_for_query( string f ) { if(sscanf(f,"%s?%s", f, query) == 2) { string v, a, b; foreach(query / "&", v) if(sscanf(v, "%s=%s", a, b) == 2) { a = http_decode_string(replace(a, "+", " ")); b = http_decode_string(replace(b, "+", " "));
a7ebda2000-01-30Per Hedbor 
b1fca01996-11-12Per Hedbor  if(variables[ a ]) variables[ a ] += "\0" + b; else variables[ a ] = b; } else if(strlen( rest_query )) rest_query += "&" + http_decode_string( v ); else rest_query = http_decode_string( v );
ebb1c51998-02-24Per Hedbor  rest_query=replace(rest_query, "+", "\000"); /* IDIOTIC STUPID STANDARD */
9211b21998-04-24Per Hedbor  }
b1fca01996-11-12Per Hedbor  return f; } private int really_set_config(array mod_config) { string url, m; string base;
e3bda32000-01-12Martin Nilsson  if (conf) base = conf->query("MyWorldLocation"); else base = "/";
b1fca01996-11-12Per Hedbor  if(supports->cookies) {
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("Setting cookie..\n");
b1fca01996-11-12Per Hedbor  if(mod_config) foreach(mod_config, m) if(m[-1]=='-') config[m[1..]]=0; else config[m]=1;
abdff21999-12-27Martin Nilsson 
b1fca01996-11-12Per Hedbor  if(sscanf(replace(raw_url,({"%3c","%3e","%3C","%3E" }), ({"<",">","<",">"})),"/<%*s>/%s",url)!=2) url = "/";
537ec81997-11-12David Hedbor  if ((base[-1] == '/') && (strlen(url) && url[0] == '/')) {
0ddff61997-07-20Henrik Grubbström (Grubba)  url = base + url[1..]; } else { url = base + url; } my_fd->write(prot + " 302 Config in cookie!\r\n"
b1fca01996-11-12Per Hedbor  "Set-Cookie: "
7dba2f2000-05-08Martin Nilsson  + Roxen.http_roxen_config_cookie(indices(config) * ",") + "\r\n"
0ddff61997-07-20Henrik Grubbström (Grubba)  "Location: " + url + "\r\n"
b1fca01996-11-12Per Hedbor  "Content-Type: text/html\r\n" "Content-Length: 0\r\n\r\n"); } else {
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("Setting {config} for user without Cookie support..\n");
b1fca01996-11-12Per Hedbor  if(mod_config) foreach(mod_config, m) if(m[-1]=='-') prestate[m[1..]]=0; else prestate[m]=1;
abdff21999-12-27Martin Nilsson  if (sscanf(replace(raw_url, ({ "%3c", "%3e", "%3C", "%3E" }),
0ddff61997-07-20Henrik Grubbström (Grubba)  ({ "<", ">", "<", ">" })), "/<%*s>/%s", url) == 2) { url = "/" + url; } if (sscanf(replace(url, ({ "%28", "%29" }), ({ "(", ")" })), "/(%*s)/%s", url) == 2) { url = "/" + url; }
7dba2f2000-05-08Martin Nilsson  url = Roxen.add_pre_state(url, prestate);
0ddff61997-07-20Henrik Grubbström (Grubba)  if (base[-1] == '/') { url = base + url[1..]; } else { url = base + url; } my_fd->write(prot + " 302 Config In Prestate!\r\n" "\r\nLocation: " + url + "\r\n" "Content-Type: text/html\r\n" "Content-Length: 0\r\n\r\n");
b1fca01996-11-12Per Hedbor  }
41b77c1999-07-15David Hedbor  return 2;
b1fca01996-11-12Per Hedbor }
0a635b1998-03-08Henrik Grubbström (Grubba) private static mixed f, line;
b5218e1999-03-18Henrik Grubbström (Grubba) private static int hstart;
0a635b1998-03-08Henrik Grubbström (Grubba) 
2aa88e2000-01-28Martin Nilsson class PrefLanguages { int decoded=0; int sorted=0; array(string) subtags=({}); array(string) languages=({}); array(float) qualities=({}); array(string) get_languages() { sort_lang(); return languages; } string get_language() {
c250bf2000-02-23Martin Nilsson  if(!languages || !sizeof(languages)) return 0;
2aa88e2000-01-28Martin Nilsson  sort_lang(); return languages[0]; } array(float) get_qualities() { sort_lang(); return qualities; } float get_quality() {
c250bf2000-02-23Martin Nilsson  if(!qualities || !sizeof(qualities)) return 0.0;
2aa88e2000-01-28Martin Nilsson  sort_lang(); return qualities[0]; } void set_sorted(array(string) lang, void|array(float) q) { languages=lang; if(q && sizeof(q)==sizeof(lang)) qualities=q; else qualities=({1.0})*sizeof(lang); sorted=1; } void sort_lang() { if(sorted && decoded) return; array(float) q;
a4ce0c2000-03-06Martin Nilsson  array(string) s=reverse(languages)-({""}), u=({});
2aa88e2000-01-28Martin Nilsson  if(!decoded) { q=({}); s=Array.map(s, lambda(string x) { float n=1.0; string sub=""; sscanf(lower_case(x), "%s;q=%f", x, n); if(n==0.0) return ""; sscanf(x, "%s-%s", x, sub); q+=({n}); u+=({sub}); return x; }); s-=({""}); decoded=1; } else q=reverse(qualities); sort(q,s,u); languages=reverse(s); qualities=reverse(q); subtags=reverse(u); sorted=1; } }
8fb48a2000-06-26David Hedbor int last;
c4ab8f1999-07-15David Hedbor private int parse_got()
b1fca01996-11-12Per Hedbor { multiset (string) sup; array mod_config;
7970471999-07-15Henrik Grubbström (Grubba)  string a, b, s="", linename, contents;
b1fca01996-11-12Per Hedbor  int config_in_url;
ebb1c51998-02-24Per Hedbor 
6a409d1999-12-27Martin Nilsson  REQUEST_WERR(sprintf("HTTP: parse_got(%O)", raw));
abdff21999-12-27Martin Nilsson  if (!method) { // Haven't parsed the first line yet.
8fb48a2000-06-26David Hedbor  int start;
7970471999-07-15Henrik Grubbström (Grubba)  // We check for \n only if \r\n fails, since Netscape 4.5 sends
abdff21999-12-27Martin Nilsson  // just a \n when doing a proxy-request.
b5218e1999-03-18Henrik Grubbström (Grubba)  // example line: // "CONNECT mikabran:443 HTTP/1.0\n" // "User-Agent: Mozilla/4.5 [en] (X11; U; Linux 2.0.35 i586)"
41b77c1999-07-15David Hedbor  // Die Netscape, die! *grumble* // Luckily the solution below shouldn't ever cause any slowdowns
8fb48a2000-06-26David Hedbor  // // Note by Neo: Rewrote the sscanf code to use search with a memory. // The reason is that otherwise it's really, REALLY easy to lock up // a Roxen server by sending a request that either has no newlines at all // or has infinite sized headers. With this version, Roxen doesn't die but // it does suck up data ad finitum - a configurable max GET request size and // also a max GET+headers would be nice. if((start = search(raw[last..], "\n")) == -1) { last = max(strlen(raw) - 3, 4);
6a409d1999-12-27Martin Nilsson  REQUEST_WERR(sprintf("HTTP: parse_got(%O): Not enough data.", raw));
41b77c1999-07-15David Hedbor  return 0;
8fb48a2000-06-26David Hedbor  } else { start += last; last = 0; if(!start) { REQUEST_WERR(sprintf("HTTP: parse_got(%O): malformed request.", raw)); return 1; // malformed request } } if (raw[start-1] == '\r') { line = raw[..start-2]; } else { // Kludge for Netscape 4.5 sending bad requests. line = raw[..start-1];
b5218e1999-03-18Henrik Grubbström (Grubba)  }
41b77c1999-07-15David Hedbor  if(strlen(line) < 4) { // Incorrect request actually - min possible (HTTP/0.9) is "GET /" // but need to support PING of course!
abdff21999-12-27Martin Nilsson 
6a409d1999-12-27Martin Nilsson  REQUEST_WERR(sprintf("HTTP: parse_got(%O): Malformed request.", raw));
41b77c1999-07-15David Hedbor  return 1;
0a635b1998-03-08Henrik Grubbström (Grubba)  }
9f85df1999-07-16Henrik Grubbström (Grubba)  string trailer, trailer_trailer; switch(sscanf(line+" ", "%s %s %s %s %s", method, f, clientprot, trailer, trailer_trailer))
41b77c1999-07-15David Hedbor  {
9f85df1999-07-16Henrik Grubbström (Grubba)  case 5: // Stupid sscanf! if (trailer_trailer != "") { // Get rid of the extra space from the sscanf above. trailer += " " + trailer_trailer[..sizeof(trailer_trailer)-2]; } /* FALL_THROUGH */
7970471999-07-15Henrik Grubbström (Grubba)  case 4:
9f85df1999-07-16Henrik Grubbström (Grubba)  // Got extra spaces in the URI. // All the extra stuff is now in the trailer.
0322b71999-08-06Per Hedbor  // Get rid of the extra space from the sscanf above. trailer = trailer[..sizeof(trailer) - 2];
9f85df1999-07-16Henrik Grubbström (Grubba)  f += " " + clientprot;
0322b71999-08-06Per Hedbor 
9f85df1999-07-16Henrik Grubbström (Grubba)  // Find the last space delimiter.
0322b71999-08-06Per Hedbor  int end;
9f85df1999-07-16Henrik Grubbström (Grubba)  if (!(end = (search(reverse(trailer), " ") + 1))) {
0322b71999-08-06Per Hedbor  // Just one space in the URI. clientprot = trailer;
9f85df1999-07-16Henrik Grubbström (Grubba)  } else {
0322b71999-08-06Per Hedbor  f += " " + trailer[..sizeof(trailer) - (end + 1)]; clientprot = trailer[sizeof(trailer) - end ..];
1afe371997-06-12Henrik Grubbström (Grubba)  }
7970471999-07-15Henrik Grubbström (Grubba)  /* FALL_THROUGH */ case 3:
41b77c1999-07-15David Hedbor  // >= HTTP/1.0
7970471999-07-15Henrik Grubbström (Grubba)  prot = clientprot; // method = upper_case(p1); if(!(< "HTTP/1.0", "HTTP/1.1" >)[prot]) { // We're nice here and assume HTTP even if the protocol // is something very weird.
41b77c1999-07-15David Hedbor  prot = "HTTP/1.1";
7970471999-07-15Henrik Grubbström (Grubba)  } // Do we have all the headers?
8fb48a2000-06-26David Hedbor  if ((end = search(raw[last..], "\r\n\r\n")) == -1) { // No, we still need more data. REQUEST_WERR("HTTP: parse_got(): Request is still not complete."); last = max(strlen(raw) - 5, 0);
41b77c1999-07-15David Hedbor  return 0; }
8fb48a2000-06-26David Hedbor  end += last; last = 0; data = raw[end+4..]; s = s[sizeof(line)+2..end-1];
2bc9081999-08-05Henrik Grubbström (Grubba)  // s now contains the unparsed headers.
41b77c1999-07-15David Hedbor  break;
9f85df1999-07-16Henrik Grubbström (Grubba)  case 2: // HTTP/0.9 clientprot = prot = "HTTP/0.9"; if(method != "PING") method = "GET"; // 0.9 only supports get. s = data = ""; // no headers or extra data... break; case 1: // PING...
abdff21999-12-27Martin Nilsson  if(method == "PING")
9f85df1999-07-16Henrik Grubbström (Grubba)  break; // only PING is valid here. return 1;
abdff21999-12-27Martin Nilsson 
7970471999-07-15Henrik Grubbström (Grubba)  default:
41b77c1999-07-15David Hedbor  // Too many or too few entries -> Hum. return 1;
0a635b1998-03-08Henrik Grubbström (Grubba)  } } else { // HTTP/1.0 or later // Check that the request is complete
8fb48a2000-06-26David Hedbor  int end; if ((end = search(raw[last..], "\r\n\r\n")) == -1) { // No, we still need more data.
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("HTTP: parse_got(): Request is still not complete.");
8fb48a2000-06-26David Hedbor  last = max(strlen(raw) - 5, 0);
0a635b1998-03-08Henrik Grubbström (Grubba)  return 0;
1afe371997-06-12Henrik Grubbström (Grubba)  }
8fb48a2000-06-26David Hedbor  end += last; data = raw[end+4..]; s = s[sizeof(line)+2..end-1];
019b711999-01-13David Hedbor  }
c4ab8f1999-07-15David Hedbor  if(method == "PING") {
abdff21999-12-27Martin Nilsson  my_fd->write("PONG\r\n");
c4ab8f1999-07-15David Hedbor  return 2; }
abdff21999-12-27Martin Nilsson 
b1fca01996-11-12Per Hedbor  raw_url = f; time = _time(1);
7970471999-07-15Henrik Grubbström (Grubba)  // if(!data) data = "";
6a409d1999-12-27Martin Nilsson  REQUEST_WERR(sprintf("RAW_URL:%O", raw_url));
7ec6601997-01-29Per Hedbor 
b1fca01996-11-12Per Hedbor  if(!remoteaddr) {
41b77c1999-07-15David Hedbor  if(my_fd) { remoteaddr = my_fd->query_address();
abdff21999-12-27Martin Nilsson  if(remoteaddr)
41b77c1999-07-15David Hedbor  sscanf(remoteaddr, "%s %*s", remoteaddr); }
e5bad21998-02-10Per Hedbor  if(!remoteaddr) {
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("HTTP: parse_request(): No remote address.");
e5bad21998-02-10Per Hedbor  end();
41b77c1999-07-15David Hedbor  return 2;
e5bad21998-02-10Per Hedbor  }
b1fca01996-11-12Per Hedbor  }
7ec6601997-01-29Per Hedbor 
6a409d1999-12-27Martin Nilsson  REQUEST_WERR(sprintf("After Remote Addr:%O", f));
abdff21999-12-27Martin Nilsson 
b1fca01996-11-12Per Hedbor  f = scan_for_query( f );
fe14f51998-08-26Henrik Grubbström (Grubba) 
6a409d1999-12-27Martin Nilsson  REQUEST_WERR(sprintf("After query scan:%O", f));
fe14f51998-08-26Henrik Grubbström (Grubba) 
fb5f681998-11-04Peter Bortas  f = http_decode_string( f );
41b77c1999-07-15David Hedbor  string prf = f[1..1]; if (prf == "<" && sscanf(f, "/<%s>/%s", a, f)==2)
b1fca01996-11-12Per Hedbor  { config_in_url = 1; mod_config = (a/",");
e5bad21998-02-10Per Hedbor  f = "/"+f;
b1fca01996-11-12Per Hedbor  }
fe14f51998-08-26Henrik Grubbström (Grubba) 
6a409d1999-12-27Martin Nilsson  REQUEST_WERR(sprintf("After cookie scan:%O", f));
abdff21999-12-27Martin Nilsson 
41b77c1999-07-15David Hedbor  if (prf == "(" && (sscanf(f, "/(%s)/%s", a, f)==2) && strlen(a))
e5bad21998-02-10Per Hedbor  {
b1fca01996-11-12Per Hedbor  prestate = aggregate_multiset(@(a/","-({""})));
e5bad21998-02-10Per Hedbor  f = "/"+f; }
fe14f51998-08-26Henrik Grubbström (Grubba) 
6a409d1999-12-27Martin Nilsson  REQUEST_WERR(sprintf("After prestate scan:%O", f));
abdff21999-12-27Martin Nilsson 
7dba2f2000-05-08Martin Nilsson  not_query = Roxen.simplify_path(f);
b1fca01996-11-12Per Hedbor 
6a409d1999-12-27Martin Nilsson  REQUEST_WERR(sprintf("After simplify_path == not_query:%O", not_query));
fe14f51998-08-26Henrik Grubbström (Grubba) 
8200cd1998-07-14Henrik Grubbström (Grubba)  request_headers = ([]); // FIXME: KEEP-ALIVE?
21dda62000-02-10Martin Nilsson  misc->pref_languages=PrefLanguages();
0a635b1998-03-08Henrik Grubbström (Grubba)  if(sizeof(s)) {
41b77c1999-07-15David Hedbor  // sscanf(s, "%s\r\n\r\n%s", s, data);
abdff21999-12-27Martin Nilsson  // s = replace(s, "\n\t", ", ") - "\r";
41b77c1999-07-15David Hedbor  // Handle rfc822 continuation lines and strip \r foreach(s/"\r\n" - ({""}), line)
b1fca01996-11-12Per Hedbor  {
6a409d1999-12-27Martin Nilsson  // REQUEST_WERR(sprintf("Header :%s", line));
41b77c1999-07-15David Hedbor  // linename=contents=0;
abdff21999-12-27Martin Nilsson 
41b77c1999-07-15David Hedbor  if(sscanf(line, "%s:%*[ \t]%s", linename, contents) == 3)
b1fca01996-11-12Per Hedbor  {
6a409d1999-12-27Martin Nilsson  REQUEST_WERR(sprintf("Header-sscanf :%s", linename));
41b77c1999-07-15David Hedbor  linename=lower_case(linename);
6a409d1999-12-27Martin Nilsson  REQUEST_WERR(sprintf("lower-case :%s", linename));
7970471999-07-15Henrik Grubbström (Grubba)  // FIXME: Multiple headers?
41b77c1999-07-15David Hedbor  request_headers[linename] = contents;
b1fca01996-11-12Per Hedbor  if(strlen(contents)) {
48633b1997-08-04Henrik Grubbström (Grubba)  switch (linename) {
ebb1c51998-02-24Per Hedbor  case "content-length":
41b77c1999-07-15David Hedbor  misc->len = (int)contents;
ebb1c51998-02-24Per Hedbor  if(!misc->len) continue;
b1fca01996-11-12Per Hedbor  if(method == "POST") {
dcc4351997-05-20Per Hedbor  if(!data) data="";
275fe21999-10-11Per Hedbor  int l = misc->len;
7ee5651996-12-10Per Hedbor  wanted_data=l; have_data=strlen(data);
ceb9271997-05-15David Hedbor 
abdff21999-12-27Martin Nilsson  if(strlen(data) < l)
275fe21999-10-11Per Hedbor  {
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("HTTP: parse_request(): More data needed in POST.");
7ee5651996-12-10Per Hedbor  return 0;
9d8ea11999-04-16Henrik Grubbström (Grubba)  }
275fe21999-10-11Per Hedbor  leftovers = data[l+2..]; data = data[..l+1];
9211b21998-04-24Per Hedbor 
fbfd721998-05-18Henrik Grubbström (Grubba)  switch(lower_case((((misc["content-type"]||"")+";")/";")[0]-" "))
86c5101997-10-05Henrik Grubbström (Grubba)  { default: // Normal form data.
7ee5651996-12-10Per Hedbor  string v; if(l < 200000) {
7a0ee61998-06-27Henrik Grubbström (Grubba)  foreach(replace(data, ({ "\n", "\r", "+" }), ({ "", "", " "}))/"&", v)
7ee5651996-12-10Per Hedbor  if(sscanf(v, "%s=%s", a, b) == 2) { a = http_decode_string( a ); b = http_decode_string( b );
a7ebda2000-01-30Per Hedbor 
7ee5651996-12-10Per Hedbor  if(variables[ a ]) variables[ a ] += "\0" + b; else variables[ a ] = b; } } break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "multipart/form-data":
57d41d1997-08-14Marcus Comstedt  object messg = MIME.Message(data, misc); foreach(messg->body_parts, object part) { if(part->disp_params->filename) { variables[part->disp_params->name]=part->getdata(); variables[part->disp_params->name+".filename"]= part->disp_params->filename; if(!misc->files) misc->files = ({ part->disp_params->name }); else misc->files += ({ part->disp_params->name }); } else {
05a7c81998-08-22Johan Schön  if(variables[part->disp_params->name]) variables[part->disp_params->name] += "\0" + part->getdata(); else variables[part->disp_params->name] = part->getdata();
7ee5651996-12-10Per Hedbor  } } break; } } break;
a7ebda2000-01-30Per Hedbor 
48633b1997-08-04Henrik Grubbström (Grubba)  case "authorization":
b1fca01996-11-12Per Hedbor  rawauth = contents; break;
a7ebda2000-01-30Per Hedbor 
48633b1997-08-04Henrik Grubbström (Grubba)  case "proxy-authorization":
c5e0961999-10-04Per Hedbor  array y; y = contents / " ";
b1fca01996-11-12Per Hedbor  if(sizeof(y) < 2) break; y[1] = decode(y[1]); misc->proxyauth=y; break;
a7ebda2000-01-30Per Hedbor 
48633b1997-08-04Henrik Grubbström (Grubba)  case "pragma":
5e89211997-02-13Per Hedbor  pragma|=aggregate_multiset(@replace(contents, " ", "")/ ",");
b1fca01996-11-12Per Hedbor  break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "user-agent":
4044612000-01-20Martin Nilsson  if(!client || !client_var->Fullname)
ebb1c51998-02-24Per Hedbor  { sscanf(contents, "%s via", contents);
f49bd32000-01-20Martin Nilsson  client_var->Fullname=contents;
ebb1c51998-02-24Per Hedbor  client = contents/" " - ({ "" }); }
b1fca01996-11-12Per Hedbor  break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "referer":
f6d62d1997-03-26Per Hedbor  referer = contents/" "; break;
a7ebda2000-01-30Per Hedbor 
41b77c1999-07-15David Hedbor  case "extension":
b1fca01996-11-12Per Hedbor #ifdef DEBUG
6a409d1999-12-27Martin Nilsson  werror("Client extension: "+contents+"\n");
b1fca01996-11-12Per Hedbor #endif
84ae131999-07-28David Hedbor  case "request-range":
d986281999-06-30David Hedbor  contents = lower_case(contents-" ");
abdff21999-12-27Martin Nilsson  if(!search(contents, "bytes"))
d986281999-06-30David Hedbor  // Only care about "byte" ranges. misc->range = contents[6..]; break;
a7ebda2000-01-30Per Hedbor 
84ae131999-07-28David Hedbor  case "range": contents = lower_case(contents-" "); if(!misc->range && !search(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;
a7ebda2000-01-30Per Hedbor 
41b77c1999-07-15David Hedbor  case "connection": case "content-type":
48633b1997-08-04Henrik Grubbström (Grubba)  misc[linename] = lower_case(contents);
b1fca01996-11-12Per Hedbor  break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "accept-encoding":
dd23d71998-03-31Henrik Grubbström (Grubba)  foreach((contents-" ")/",", string e) { if (lower_case(e) == "gzip") { supports["autogunzip"] = 1; } } case "accept":
48633b1997-08-04Henrik Grubbström (Grubba)  case "accept-charset": case "session-id": case "message-id": case "from":
b1fca01996-11-12Per Hedbor  if(misc[linename])
5e89211997-02-13Per Hedbor  misc[linename] += (contents-" ") / ",";
b1fca01996-11-12Per Hedbor  else
5e89211997-02-13Per Hedbor  misc[linename] = (contents-" ") / ",";
b1fca01996-11-12Per Hedbor  break;
06cc9a2000-01-23Martin Nilsson  case "accept-language": array alang=(contents-" ") / ","; if(misc["accept-language"]) misc["accept-language"] += alang;
21dda62000-02-10Martin Nilsson  else
06cc9a2000-01-23Martin Nilsson  misc["accept-language"] = alang;
2aa88e2000-01-28Martin Nilsson  misc->pref_languages->languages=misc["accept-language"];
06cc9a2000-01-23Martin Nilsson  break;
b1fca01996-11-12Per Hedbor  case "cookie": /* This header is quite heavily parsed */ string c; misc->cookies = contents;
5e5d6a1998-04-30Henrik Grubbström (Grubba)  if (!sizeof(contents)) { // Needed for the new Pike 0.6 break; } foreach(((contents/";") - ({""})), c)
b1fca01996-11-12Per Hedbor  { string name, value;
5e5d6a1998-04-30Henrik Grubbström (Grubba)  while(sizeof(c) && c[0]==' ') c=c[1..];
b1fca01996-11-12Per Hedbor  if(sscanf(c, "%s=%s", name, value) == 2) { value=http_decode_string(value); name=http_decode_string(name); cookies[ name ]=value; if(name == "RoxenConfig" && strlen(value)) { array tmpconfig = value/"," + ({ }); string m; if(mod_config && sizeof(mod_config)) foreach(mod_config, m) if(!strlen(m)) { continue; } /* Bug in parser force { and } */ else if(m[0]=='-') tmpconfig -= ({ m[1..] }); else tmpconfig |= ({ m }); mod_config = 0; config = aggregate_multiset(@tmpconfig); } } } break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "host": case "proxy-connection": case "security-scheme":
6e89ab1998-08-10Per Hedbor  case "via": case "cache-control": case "negotiate": case "forwarded":
9b10a41998-10-18Henrik Grubbström (Grubba)  case "new-uri":
6e89ab1998-08-10Per Hedbor  misc[linename]=contents;
a7ebda2000-01-30Per Hedbor  break;
b1fca01996-11-12Per Hedbor 
48633b1997-08-04Henrik Grubbström (Grubba)  case "proxy-by": case "proxy-maintainer": case "proxy-software": case "mime-version":
b1fca01996-11-12Per Hedbor  break;
a7ebda2000-01-30Per Hedbor 
4ee9601999-07-02David Hedbor  case "if-modified-since":
ebb1c51998-02-24Per Hedbor  since=contents;
b1fca01996-11-12Per Hedbor  break; } } }
41b77c1999-07-15David Hedbor  }
b1fca01996-11-12Per Hedbor  }
41b77c1999-07-15David Hedbor 
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("HTTP: parse_got(): after header scan");
abdff21999-12-27Martin Nilsson #ifndef DISABLE_SUPPORTS
41b77c1999-07-15David Hedbor  if(!client) { client = ({ "unknown" });
338ffa2000-02-15Martin Nilsson  array s_and_v = find_supports_and_vars("", supports); supports = s_and_v[0]; client_var = s_and_v[1];
f49bd32000-01-20Martin Nilsson  } else {
3582882000-03-24Per Hedbor  if( !client_var->Fullname ) client_var->Fullname = "unknown";
f49bd32000-01-20Martin Nilsson  client_var->fullname=lower_case(client_var->Fullname);
338ffa2000-02-15Martin Nilsson  array s_and_v = find_supports_and_vars(client_var->fullname, supports, client_var); supports = s_and_v[0]; client_var = s_and_v[1];
f49bd32000-01-20Martin Nilsson  }
8d16021999-11-16Henrik Grubbström (Grubba) 
653d7d2000-02-14Per Hedbor  if ( client_var->charset && client_var->charset != "iso-8859-1" ) { set_output_charset( client_var->charset ); input_charset = client_var->charset;
8d16021999-11-16Henrik Grubbström (Grubba)  }
41b77c1999-07-15David Hedbor #else supports = (< "images", "gifinline", "forms", "mailto">); #endif
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("HTTP: parse_got(): supports");
ebb1c51998-02-24Per Hedbor  if(!referer) referer = ({ });
dde9651996-12-08David Hedbor  if(misc->proxyauth) { // The Proxy-authorization header should be removed... So there. mixed tmp1,tmp2;
abdff21999-12-27Martin Nilsson 
dde9651996-12-08David Hedbor  foreach(tmp2 = (raw / "\n"), tmp1) { if(!search(lower_case(tmp1), "proxy-authorization:")) tmp2 -= ({tmp1}); }
abdff21999-12-27Martin Nilsson  raw = tmp2 * "\n";
b1fca01996-11-12Per Hedbor  }
ceb9271997-05-15David Hedbor  if(config_in_url) {
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("HTTP: parse_got(): config_in_url");
ceb9271997-05-15David Hedbor  return really_set_config( mod_config ); }
b1fca01996-11-12Per Hedbor  if(!supports->cookies) config = prestate; else
7dba2f2000-05-08Martin Nilsson  if(port_obj->query("set_cookie")
27b0e11996-11-26Per Hedbor  && !cookies->RoxenUserID && strlen(not_query)
ebb1c51998-02-24Per Hedbor  && not_query[0]=='/' && method!="PUT")
b1fca01996-11-12Per Hedbor  {
ab9b6d2000-02-11Per Hedbor  if (!(port_obj->query("set_cookie_only_once") &&
a773c61997-07-06Henrik Grubbström (Grubba)  cache_lookup("hosts_for_cookie",remoteaddr))) {
7dba2f2000-05-08Martin Nilsson  misc->moreheads = ([ "Set-Cookie":Roxen.http_roxen_id_cookie(), ]);
a773c61997-07-06Henrik Grubbström (Grubba)  }
ab9b6d2000-02-11Per Hedbor  if (port_obj->query("set_cookie_only_once"))
a773c61997-07-06Henrik Grubbström (Grubba)  cache_set("hosts_for_cookie",remoteaddr,1);
b1fca01996-11-12Per Hedbor  }
41b77c1999-07-15David Hedbor  return 3; // Done.
b1fca01996-11-12Per Hedbor } void disconnect() {
ebb1c51998-02-24Per Hedbor  file = 0;
943da91999-09-26Martin Stjernholm #ifdef REQUEST_DEBUG if (my_fd) MARK_FD("my_fd in HTTP disconnected?"); #endif
35ed001998-03-26Per Hedbor  if(do_not_disconnect)return;
14179b1997-01-29Per Hedbor  destruct();
b1fca01996-11-12Per Hedbor }
ebb1c51998-02-24Per Hedbor void end(string|void s, int|void keepit)
b1fca01996-11-12Per Hedbor {
903e571998-03-25Per Hedbor  pipe = 0;
8afc811998-02-04Per Hedbor #ifdef PROFILE if(conf) { float elapsed = SECHR(HRTIME()-req_time);
3117791998-08-20Per Hedbor  string nid = #ifdef FILE_PROFILE not_query #else dirname(not_query) #endif ;
8afc811998-02-04Per Hedbor  array p;
3117791998-08-20Per Hedbor  if(!(p=conf->profile_map[nid])) p = conf->profile_map[nid] = ({0,0.0,0.0});
10f7971999-04-22Per Hedbor  p[0]++;
8afc811998-02-04Per Hedbor  p[1] += elapsed; if(elapsed > p[2]) p[2]=elapsed; }
ceb9271997-05-15David Hedbor #endif
ebb1c51998-02-24Per Hedbor  #ifdef KEEP_ALIVE
49c07e2000-01-17Henrik Grubbström (Grubba)  if(keepit && !file->raw
41b77c1999-07-15David Hedbor  && (misc->connection == "keep-alive" || (prot == "HTTP/1.1" && misc->connection != "close"))
ebb1c51998-02-24Per Hedbor  && my_fd) { // Now.. Transfer control to a new http-object. Reset all variables etc..
e554ae1999-12-26Johan Sundström  object o = object_program(this_object())(my_fd, port_obj);
ebb1c51998-02-24Per Hedbor  o->remoteaddr = remoteaddr; o->supports = supports; o->host = host; o->client = client;
3235691998-03-26Per Hedbor  MARK_FD("HTTP kept alive");
ebb1c51998-02-24Per Hedbor  object fd = my_fd; my_fd=0;
903e571998-03-25Per Hedbor  if(s) leftovers += s;
870a9e1999-10-04Per Hedbor  o->chain(fd,port_obj,leftovers);
ebb1c51998-02-24Per Hedbor  disconnect(); return; } #endif
e8790b1998-02-19Per Hedbor  if(objectp(my_fd)) {
3235691998-03-26Per Hedbor  MARK_FD("HTTP closed");
ebb1c51998-02-24Per Hedbor  catch { my_fd->set_close_callback(0); my_fd->set_read_callback(0);
3235691998-03-26Per Hedbor  my_fd->set_blocking();
ebb1c51998-02-24Per Hedbor  if(s) my_fd->write(s);
e8790b1998-02-19Per Hedbor  my_fd->close(); destruct(my_fd);
ebb1c51998-02-24Per Hedbor  };
e8790b1998-02-19Per Hedbor  my_fd = 0; }
abdff21999-12-27Martin Nilsson  disconnect();
b1fca01996-11-12Per Hedbor }
9211b21998-04-24Per Hedbor static void do_timeout()
b1fca01996-11-12Per Hedbor {
beb4b21998-06-02Per Hedbor  // werror("do_timeout() called, time="+time+"; time()="+_time()+"\n");
ebb1c51998-02-24Per Hedbor  int elapsed = _time()-time;
9211b21998-04-24Per Hedbor  if(time && elapsed >= 30)
8afc811998-02-04Per Hedbor  {
3235691998-03-26Per Hedbor  MARK_FD("HTTP timeout");
beb4b21998-06-02Per Hedbor  // Do not under any circumstances send any data as a reply here. // This is an easy reason why: It breaks keep-alive totaly. // It is not a very good idea to do that, since it might be enabled // per deafult any century now.. end("");
8afc811998-02-04Per Hedbor  } else {
ebb1c51998-02-24Per Hedbor  // premature call_out... *¤#!"
8afc811998-02-04Per Hedbor  call_out(do_timeout, 10);
3235691998-03-26Per Hedbor  MARK_FD("HTTP premature timeout");
8afc811998-02-04Per Hedbor  }
b1fca01996-11-12Per Hedbor }
35ed001998-03-26Per Hedbor static string last_id, last_from; string get_id(string from) { if(last_from == from) return last_id; last_from=from; catch { object f = open(from,"r"); string id;
2827392000-03-26Martin Stjernholm  id = f->read(1024);
35ed001998-03-26Per Hedbor  if(sscanf(id, "%*s$"+"Id: %*s,v %s ", id) == 3)
0b651e1998-03-26Per Hedbor  return last_id=" (version "+id+")";
35ed001998-03-26Per Hedbor  }; last_id = ""; return ""; }
8641d42000-03-24Martin Stjernholm void add_id(mixed to)
35ed001998-03-26Per Hedbor {
2827392000-03-26Martin Stjernholm  if (arrayp (to) && sizeof (to) >= 2 && arrayp (to[1]) || objectp (to) && to->is_generic_error)
8641d42000-03-24Martin Stjernholm  foreach(to[1], array q)
2827392000-03-26Martin Stjernholm  if(sizeof(q) && stringp(q[0])) { string id = get_id(q[0]); catch (q[0] += id); }
35ed001998-03-26Per Hedbor }
8641d42000-03-24Martin Stjernholm string link_to(string file, int line, string fun, int eid, int qq)
86e77d1998-05-07Per Hedbor {
8641d42000-03-24Martin Stjernholm  if (!file || !line) return "<a>"; if(file[0]!='/') file = combine_path(getcwd(), file);
4f6d922000-03-27Johan Sundström  return ("<a href=\"/(old_error,find_file)/error/?"+
7dba2f2000-05-08Martin Nilsson  "file="+Roxen.http_encode_string(file)+ (fun ? "&fun="+Roxen.http_encode_string(fun) : "") +
8641d42000-03-24Martin Stjernholm  "&off="+qq+ "&error="+eid+ (line ? "&line="+line+"#here" : "") + "\">");
86e77d1998-05-07Per Hedbor }
8641d42000-03-24Martin Stjernholm static string error_page_header (string title) {
7dba2f2000-05-08Martin Nilsson  title = Roxen.html_encode_string (title);
8641d42000-03-24Martin Stjernholm  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>
7dba2f2000-05-08Martin Nilsson <td align='right'><font size='+1'>Roxen WebServer " + Roxen.html_encode_string (roxen_version()) + #"</font></td>
8641d42000-03-24Martin Stjernholm </tr></table> "; }
86e77d1998-05-07Per Hedbor 
8641d42000-03-24Martin Stjernholm string format_backtrace(int eid)
2b9d4b1998-03-25Per Hedbor {
8641d42000-03-24Martin Stjernholm  [string msg, array(string) rxml_bt, array(array) bt,
2827392000-03-26Martin Stjernholm  string raw_bt_descr, string raw_url, string raw] =
8641d42000-03-24Martin Stjernholm  roxen.query_var ("errors")[eid]; string res = error_page_header ("Internal Server Error") +
7dba2f2000-05-08Martin Nilsson  "<h1>" + replace (Roxen.html_encode_string (msg), "\n", "<br />\n") + "</h1>\n";
8641d42000-03-24Martin Stjernholm  if (rxml_bt && sizeof (rxml_bt)) { res += "<h3>RXML frame backtrace</h3>\n<ul>\n"; foreach (rxml_bt, string line)
7dba2f2000-05-08Martin Nilsson  res += "<li>" + Roxen.html_encode_string (line) + "</li>\n";
8641d42000-03-24Martin Stjernholm  res += "</ul>\n\n"; }
c658b92000-03-24Martin Stjernholm  if (bt && sizeof (bt)) {
8641d42000-03-24Martin Stjernholm  res += "<h3>Pike backtrace</h3>\n<ul>\n"; int q = sizeof (bt); foreach(reverse (bt), [string file, int line, string func, string descr]) res += "<li value="+(q--)+">" + link_to (file, line, func, eid, q) +
7dba2f2000-05-08Martin Nilsson  (file ? Roxen.html_encode_string (file) : "<i>Unknown program</i>") +
8641d42000-03-24Martin Stjernholm  (line ? ":" + line : "") +
7dba2f2000-05-08Martin Nilsson  "</a>" + (file ? Roxen.html_encode_string (get_id (file)) : "") + ":<br />\n" + replace (Roxen.html_encode_string (descr),
8641d42000-03-24Martin Stjernholm  ({"(", ")"}), ({"<b>(</b>", "<b>)</b>"})) + "</li>\n"; res += "</ul>\n\n";
2b9d4b1998-03-25Per Hedbor  }
8641d42000-03-24Martin Stjernholm 
4f6d922000-03-27Johan Sundström  res += ("<p><b><a href=\"/(old_error,plain)/error/?error="+eid+"\">"
8641d42000-03-24Martin Stjernholm  "Generate text only version of this error message, for bug reports"+ "</a></b></p>\n\n");
258bb12000-03-15Martin Nilsson  return res+"</body></html>";
2b9d4b1998-03-25Per Hedbor }
b1fca01996-11-12Per Hedbor 
8641d42000-03-24Martin Stjernholm string generate_bugreport(string msg, array(string) rxml_bt, array(string) bt,
2827392000-03-26Martin Stjernholm  string raw_bt_descr, string raw_url, string raw)
2b9d4b1998-03-25Per Hedbor {
2827392000-03-26Martin Stjernholm  return ("Roxen version: "+version()+ (roxen.real_version != version()? " ("+roxen.real_version+")":"")+ "\nPike version: " + predef::version() + "\nRequested URL: "+raw_url+"\n" "\nError: " + raw_bt_descr + "\nRequest data:\n"+raw);
f288181998-05-25Per Hedbor } string censor(string what) { string a, b, c;
0322b71999-08-06Per Hedbor  if(!what) return "No backtrace";
8641d42000-03-24Martin Stjernholm  if(sscanf(what, "%suthorization:%s\n%s", a, b, c)==3) return a+"uthorization: ################ (censored)\n"+c;
f288181998-05-25Per Hedbor  return what;
2b9d4b1998-03-25Per Hedbor }
b1fca01996-11-12Per Hedbor 
8641d42000-03-24Martin Stjernholm int store_error(mixed err)
86e77d1998-05-07Per Hedbor {
35c2631999-02-16Per Hedbor  mapping e = roxen.query_var("errors"); if(!e) roxen.set_var("errors", ([])); e = roxen.query_var("errors"); /* threads... */
abdff21999-12-27Martin Nilsson 
86e77d1998-05-07Per Hedbor  int id = ++e[0]; if(id>1024) id = 1;
8641d42000-03-24Martin Stjernholm  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) { rxml_bt = (msg[i + sizeof ("\nRXML frame backtrace:")..] / "\n | ")[1..]; if (sizeof (rxml_bt)) rxml_bt[-1] = rxml_bt[-1][..sizeof (rxml_bt[-1]) - 2]; msg = msg[..i - 1]; } } string cwd = getcwd() + "/"; array bt;
c658b92000-03-24Martin Stjernholm  if (arrayp (err) && sizeof (err) >= 2 && arrayp (err[1]) || objectp (err) && err->is_generic_error) {
8641d42000-03-24Martin Stjernholm  bt = ({}); foreach (err[1], mixed ent) { string file, func, descr; int line; if (arrayp (ent)) { if (sizeof (ent) && stringp (ent[0])) if (ent[0][..sizeof (cwd) - 1] == cwd) file = ent[0] = ent[0][sizeof (cwd)..]; else file = ent[0]; if (sizeof (ent) >= 2) line = ent[1]; if (sizeof (ent) >= 3) if(functionp(ent[2])) { func = function_name(ent[2]); if (!file) catch { file = master()->describe_program ( object_program (function_object (ent[2]))); if (file[..sizeof (cwd) - 1] == cwd) file = file[sizeof (cwd)..]; }; } else if (stringp(ent[2])) func = ent[2]; else func ="unknown function"; if (sizeof (ent) >= 4) descr = func + "(" + master()->stupid_describe_comma_list ( ent[3..], master()->bt_max_string_len) + ")"; else descr = func + "()"; } else if (stringp (ent)) descr = ent; else if (catch (descr = sprintf ("%O", ent))) descr = "???"; bt += ({({file, line, func, descr})}); } }
2827392000-03-26Martin Stjernholm  add_id (err); e[id] = ({msg,rxml_bt,bt,describe_backtrace (err),raw_url,censor(raw)});
86e77d1998-05-07Per Hedbor  return id; } array get_error(string eid) {
35c2631999-02-16Per Hedbor  mapping e = roxen.query_var("errors");
86e77d1998-05-07Per Hedbor  if(e) return e[(int)eid]; return 0; }
2b9d4b1998-03-25Per Hedbor void internal_error(array err)
b1fca01996-11-12Per Hedbor {
bd22231998-03-27David Hedbor  array err2;
ab9b6d2000-02-11Per Hedbor  if(port_obj->query("show_internals"))
86e77d1998-05-07Per Hedbor  {
abdff21999-12-27Martin Nilsson  err2 = catch {
7dba2f2000-05-08Martin Nilsson  file = Roxen.http_low_answer(500, format_backtrace(store_error(err)));
a7ebda2000-01-30Per Hedbor  };
bd22231998-03-27David Hedbor  if(err2) { werror("Internal server error in internal_error():\n" +
86e77d1998-05-07Per Hedbor  describe_backtrace(err2)+"\n while processing \n"+
bd22231998-03-27David Hedbor  describe_backtrace(err));
7dba2f2000-05-08Martin Nilsson  file = Roxen.http_low_answer(500, "<h1>Error: The server failed to "
bd22231998-03-27David Hedbor  "fulfill your query, due to an "
86e77d1998-05-07Per Hedbor  "internal error in the internal error routine.</h1>");
2b9d4b1998-03-25Per Hedbor  }
f788481998-03-18Henrik Grubbström (Grubba)  } else {
7dba2f2000-05-08Martin Nilsson  file = Roxen.http_low_answer(500, "<h1>Error: The server failed to "
2b9d4b1998-03-25Per Hedbor  "fulfill your query, due to an internal error.</h1>");
f788481998-03-18Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor  report_error("Internal server error: " + describe_backtrace(err) + "\n");
59f65b2000-01-05Henrik Grubbström (Grubba) #ifdef INTERNAL_ERROR_DEBUG report_error(sprintf("Raw backtrace:%O\n", err)); #endif /* INTERNAL_ERROR_DEBUG */
b1fca01996-11-12Per Hedbor }
2b40f41999-12-29Martin Stjernholm // This macro ensures that something gets reported even when the very // call to internal_error() fails. That happens eg when this_object() // has been destructed. #define INTERNAL_ERROR(err) \ if (mixed __eRr = catch (internal_error (err))) \ report_error("Internal server error: " + describe_backtrace(err) + \ "internal_error() also failed: " + describe_backtrace(__eRr))
23a7891996-12-15Per Hedbor int wants_more() { return !!cache; }
beaca01998-02-20Per Hedbor void do_log()
d7b0871997-08-31Per Hedbor {
3235691998-03-26Per Hedbor  MARK_FD("HTTP logging"); // fd can be closed here
d7b0871997-08-31Per Hedbor  if(conf) { int len;
ebb1c51998-02-24Per Hedbor  if(pipe) file->len = pipe->bytes_sent();
d7b0871997-08-31Per Hedbor  if(conf) { if(file->len > 0) conf->sent+=file->len;
d986281999-06-30David Hedbor  file->len += misc->_log_cheat_addition;
f7d9811997-09-12Per Hedbor  conf->log(file, this_object());
d7b0871997-08-31Per Hedbor  } }
903e571998-03-25Per Hedbor  end(0,1);
e5bad21998-02-10Per Hedbor  return;
d7b0871997-08-31Per Hedbor }
fa44bd1998-03-28David Hedbor #ifdef FD_DEBUG
4949d71998-03-28David Hedbor void timer(int start) {
aa92c11998-08-20Henrik Grubbström (Grubba)  if(pipe) { // FIXME: Disconnect if no data has been sent for a long while // (30min?)
42ba931998-03-28David Hedbor  MARK_FD(sprintf("HTTP_piping_%d_%d_%d_%d_(%s)",
2c499e1998-03-28David Hedbor  pipe->sent, stringp(pipe->current_input) ?
b529001998-03-28David Hedbor  strlen(pipe->current_input) : -1,
9feab41998-03-28David Hedbor  pipe->last_called,
abdff21999-12-27Martin Nilsson  _time(1) - start,
42ba931998-03-28David Hedbor  not_query));
aa92c11998-08-20Henrik Grubbström (Grubba)  } else {
42ba931998-03-28David Hedbor  MARK_FD("HTTP piping, but no pipe for "+not_query);
aa92c11998-08-20Henrik Grubbström (Grubba)  }
4949d71998-03-28David Hedbor  call_out(timer, 30, start); }
fa44bd1998-03-28David Hedbor #endif
86e77d1998-05-07Per Hedbor 
8641d42000-03-24Martin Stjernholm string handle_error_file_request (string msg, array(string) rxml_bt, array(array) bt,
2827392000-03-26Martin Stjernholm  string raw_bt_descr, string raw_url, string raw)
86e77d1998-05-07Per Hedbor { string data = Stdio.read_bytes(variables->file);
8641d42000-03-24Martin Stjernholm  if(!data) return error_page_header (variables->file) + "<h3><i>Source file could not be read</i></h3>\n" "</body></html>";
86e77d1998-05-07Per Hedbor  string down;
8641d42000-03-24Martin Stjernholm  int next = (int) variables->off + 1;
86e77d1998-05-07Per Hedbor 
8641d42000-03-24Martin Stjernholm  if(next < sizeof (bt)) { [string file, int line, string func, string descr] = bt[next]; down = link_to (file, line, func, (int) variables->error, next); }
86e77d1998-05-07Per Hedbor  else down = "<a>";
8641d42000-03-24Martin Stjernholm  int off = 49; array (string) lines = data/"\n"; int start = (int)variables->line-50; if(start < 0)
86e77d1998-05-07Per Hedbor  {
8641d42000-03-24Martin Stjernholm  off += start; start = 0; } int end = (int)variables->line+50;
86e77d1998-05-07Per Hedbor 
8641d42000-03-24Martin Stjernholm  // The highlighting doesn't work well enough on recent pike code. //lines=highlight_pike("foo", ([ "nopre":1 ]), lines[start..end]*"\n")/"\n";
7dba2f2000-05-08Martin Nilsson  lines = map (lines[start..end], Roxen.html_encode_string);
8641d42000-03-24Martin Stjernholm  if(sizeof(lines)>off) { sscanf (lines[off], "%[ \t]%s", string indent, string code); if (!sizeof (code)) code = "&nbsp;"; lines[off] = indent + "<font size='+1'><b>"+down+code+"</a></b></font></a>";
86e77d1998-05-07Per Hedbor  }
8641d42000-03-24Martin Stjernholm  lines[max(off-20,0)] = "<a name=here>"+lines[max(off-20,0)]+"</a>";
abdff21999-12-27Martin Nilsson 
8641d42000-03-24Martin Stjernholm  return error_page_header (variables->file) + "<font size='-1'><pre>" + lines*"\n" + "</pre></font>\n" "</body></html>";
86e77d1998-05-07Per Hedbor }
d986281999-06-30David Hedbor // The wrapper for multiple ranges (send a multipart/byteranges reply). #define BOUND "Byte_Me_Now_Roxen" class MultiRangeWrapper { object file; function rcb; int current_pos, len, separator; array ranges; array range_info = ({}); string type; string stored_data = "";
84ae131999-07-28David Hedbor  void create(mapping _file, mapping heads, array _ranges, object id)
d986281999-06-30David Hedbor  { file = _file->file; len = _file->len; foreach(indices(heads), string h) { if(lower_case(h) == "content-type") { type = heads[h]; m_delete(heads, h); } }
84ae131999-07-28David Hedbor  if(id->request_headers["request-range"]) heads["Content-Type"] = "multipart/x-byteranges; boundary=" BOUND; else heads["Content-Type"] = "multipart/byteranges; boundary=" BOUND;
d986281999-06-30David Hedbor  ranges = _ranges; int clen; foreach(ranges, array range) { int rlen = 1+ range[1] - range[0];
84ae131999-07-28David Hedbor  string sep = sprintf("\r\n--" BOUND "\r\nContent-Type: %s\r\n"
d986281999-06-30David Hedbor  "Content-Range: bytes %d-%d/%d\r\n\r\n",
84ae131999-07-28David Hedbor  type||"application/octet-stream", @range, len);
d986281999-06-30David Hedbor  clen += rlen + strlen(sep); range_info += ({ ({ rlen, sep }) }); } clen += strlen(BOUND) + 8; // End boundary length. _file->len = clen; } string read(int num_bytes) { string out = stored_data; int rlen, total = num_bytes; num_bytes -= strlen(out);
037f4b1999-07-04David Hedbor  stored_data = "";
d986281999-06-30David Hedbor  foreach(ranges, array range) { rlen = range_info[0][0] - current_pos; if(separator != 1) { // New range, write new separator.
4ee9601999-07-02David Hedbor  // write("Initiating new range %d -> %d.\n", @range);
d986281999-06-30David Hedbor  out += range_info[0][1]; num_bytes -= strlen(range_info[0][1]); file->seek(range[0]); separator = 1; } if(num_bytes > 0) { if(rlen <= num_bytes) // Entire range fits. { out += file->read(rlen); num_bytes -= rlen; current_pos = separator = 0; ranges = ranges[1..]; // One range done. range_info = range_info[1..]; } else { out += file->read(num_bytes); current_pos += num_bytes; num_bytes = 0; } }
30f0321999-07-04David Hedbor  if(num_bytes <= 0) break; // Return data
d986281999-06-30David Hedbor  }
037f4b1999-07-04David Hedbor  if(!sizeof(ranges) && separator != 2) { // End boundary. Only write once and only when // no more ranges remain.
d986281999-06-30David Hedbor  separator = 2; out += "\r\n--" BOUND "--\r\n";
abdff21999-12-27Martin Nilsson  }
d986281999-06-30David Hedbor  if(strlen(out) > total) { // Oops. too much data again. Write and store. Write and store. stored_data = out[total..]; return out[..total-1]; } return out ; // We are finally done. }
abdff21999-12-27Martin Nilsson 
bceeba1999-07-05Henrik Grubbström (Grubba)  mixed `->(string what) {
d986281999-06-30David Hedbor  switch(what) { case "read": return read; case "set_nonblocking": return 0;
30f0321999-07-04David Hedbor  case "query_fd":
bceeba1999-07-05Henrik Grubbström (Grubba)  return lambda() { return -1; };
abdff21999-12-27Martin Nilsson 
d986281999-06-30David Hedbor  default: return file[what]; } } } // Parse the range header itno multiple ranges. array parse_range_header(int len) { array ranges = ({}); foreach(misc->range / ",", string range) { int r1, r2; if(range[0] == '-' ) { // End of file request r1 = (len - (int)range[1..]); if(r1 < 0) {
abdff21999-12-27Martin Nilsson  // Entire file requested here.
d986281999-06-30David Hedbor  r1 = 0; }
abdff21999-12-27Martin Nilsson  ranges += ({ ({ len - (int)range[1..], len-1 }) });
d986281999-06-30David Hedbor  } else if(range[-1] == '-') { // Rest of file request r1 = (int)range; if(r1 >= len) // Range beginning is after EOF.
abdff21999-12-27Martin Nilsson  continue;
d986281999-06-30David Hedbor  ranges += ({ ({ r1, len-1 }) }); } else if(sscanf(range, "%d-%d", r1, r2)==2) { // Standard range if(r1 <= r2) { if(r1 >= len) // Range beginning is after EOF. continue; ranges += ({ ({ r1, r2 < len ? r2 : len -1 }) }); }
abdff21999-12-27Martin Nilsson  else
d986281999-06-30David Hedbor  // A syntatically incorrect range should make the server // ignore the header. Really. return 0; } else // Invalid syntax again...
abdff21999-12-27Martin Nilsson  return 0;
d986281999-06-30David Hedbor  } return ranges; }
45b22b1998-10-26Henrik Grubbström (Grubba) // Tell the client that it can start sending some more data void ready_to_receive() { if (clientprot == "HTTP/1.1") { my_fd->write("HTTP/1.1 100 Continue\r\n"); } }
aa92c11998-08-20Henrik Grubbström (Grubba) 
d943c81998-05-18Henrik Grubbström (Grubba) // Send the result. void send_result(mapping|void result)
b1fca01996-11-12Per Hedbor {
d943c81998-05-18Henrik Grubbström (Grubba)  array err;
5bc1991997-01-29Per Hedbor  int tmp;
b1fca01996-11-12Per Hedbor  mapping heads;
5bc1991997-01-29Per Hedbor  string head_string;
d943c81998-05-18Henrik Grubbström (Grubba)  if (result) { file = result;
14179b1997-01-29Per Hedbor  }
82f5191997-03-02Per Hedbor 
6a409d1999-12-27Martin Nilsson  REQUEST_WERR(sprintf("HTTP: send_result(%O)", file));
abdff21999-12-27Martin Nilsson 
b1fca01996-11-12Per Hedbor  if(!mappingp(file)) {
f6d62d1997-03-26Per Hedbor  if(misc->error_code)
7dba2f2000-05-08Martin Nilsson  file = Roxen.http_low_answer(misc->error_code, errors[misc->error]);
d943c81998-05-18Henrik Grubbström (Grubba)  else if(err = catch {
7dba2f2000-05-08Martin Nilsson  file = Roxen.http_low_answer(404, Roxen.parse_rxml(
f206922000-03-07Martin Stjernholm #ifdef OLD_RXML_COMPAT
7dba2f2000-05-08Martin Nilsson  replace(conf->query("ZNoSuchFile"), ({"$File", "$Me"}), ({ "&page.virtfile;", conf->query("MyWorldLocation") })),
f206922000-03-07Martin Stjernholm #else
7dba2f2000-05-08Martin Nilsson  conf->query("ZNoSuchFile"),
f206922000-03-07Martin Stjernholm #endif
7dba2f2000-05-08Martin Nilsson  this_object()));
d943c81998-05-18Henrik Grubbström (Grubba)  }) {
2b40f41999-12-29Martin Stjernholm  INTERNAL_ERROR(err);
d943c81998-05-18Henrik Grubbström (Grubba)  }
14179b1997-01-29Per Hedbor  } else {
abdff21999-12-27Martin Nilsson  if((file->file == -1) || file->leave_me)
b1fca01996-11-12Per Hedbor  {
903e571998-03-25Per Hedbor  if(do_not_disconnect) { file = 0; pipe = 0; return; }
aa92c11998-08-20Henrik Grubbström (Grubba)  my_fd = 0; file = 0;
14179b1997-01-29Per Hedbor  return;
b1fca01996-11-12Per Hedbor  }
ebb1c51998-02-24Per Hedbor  if(file->type == "raw") file->raw = 1; else if(!file->type) file->type="text/plain";
14179b1997-01-29Per Hedbor  }
abdff21999-12-27Martin Nilsson 
41b77c1999-07-15David Hedbor  if(!file->raw)
b1fca01996-11-12Per Hedbor  {
41b77c1999-07-15David Hedbor  heads = ([]);
b1fca01996-11-12Per Hedbor  if(!file->len) { if(objectp(file->file))
14179b1997-01-29Per Hedbor  if(!file->stat && !(file->stat=misc->stat))
7568ae2000-02-17Per Hedbor  file->stat = (array(int))file->file->stat();
b1fca01996-11-12Per Hedbor  array fstat; if(arrayp(fstat = file->stat)) { if(file->file && !file->len) file->len = fstat[1];
abdff21999-12-27Martin Nilsson 
6692f31999-12-06Henrik Grubbström (Grubba)  if (fstat[3] > misc->last_modified) { misc->last_modified = fstat[3];
fc5d571999-12-06Henrik Grubbström (Grubba)  }
41b77c1999-07-15David Hedbor  if(prot != "HTTP/0.9") {
7dba2f2000-05-08Martin Nilsson  heads["Last-Modified"] = Roxen.http_date(misc->last_modified);
a7ebda2000-01-30Per Hedbor 
41b77c1999-07-15David Hedbor  if(since)
b1fca01996-11-12Per Hedbor  {
fc5d571999-12-06Henrik Grubbström (Grubba)  /* ({ time, len }) */
66faaf2000-03-19Martin Nilsson  array(int) since_info = Roxen.parse_since(since);
6692f31999-12-06Henrik Grubbström (Grubba)  if ((since_info[0] >= misc->last_modified) &&
9e62741999-12-06Henrik Grubbström (Grubba)  (since_info[0] + misc->cacheable >= predef::time(1)) &&
fc5d571999-12-06Henrik Grubbström (Grubba)  ((since_info[1] == -1) || (since_info[1] == file->len)))
41b77c1999-07-15David Hedbor  { file->error = 304; file->file = 0; file->data=""; // method=""; }
b1fca01996-11-12Per Hedbor  } } } }
41b77c1999-07-15David Hedbor  if(prot != "HTTP/0.9") {
1c80332000-02-17Per Hedbor  string h, charset="";
0b0d082000-02-14Per Hedbor  if( stringp(file->data) ) {
3582882000-03-24Per Hedbor  if (file["type"][0..4] == "text/") {
65301c2000-03-06Jonas Wallden  [charset,file->data] = output_encode( file->data ); if( charset ) charset = "; charset="+charset; else charset = ""; }
62caea2000-02-14Per Hedbor  if(stringp(file->data))
d7978b2000-02-14Per Hedbor  file->len = strlen(file->data);
0b0d082000-02-14Per Hedbor  } heads |= ([
41b77c1999-07-15David Hedbor  "MIME-Version" : (file["mime-version"] || "1.0"),
0b0d082000-02-14Per Hedbor  "Content-type" : file["type"]+charset,
41b77c1999-07-15David Hedbor  "Accept-Ranges" : "bytes", "Server" : replace(version(), " ", "·"), #ifdef KEEP_ALIVE "Connection": (misc->connection == "close" ? "close": "Keep-Alive"), #else "Connection" : "close", #endif
7dba2f2000-05-08Martin Nilsson  "Date" : Roxen.http_date(time)
41b77c1999-07-15David Hedbor  ]);
d986281999-06-30David Hedbor 
b1fca01996-11-12Per Hedbor 
41b77c1999-07-15David Hedbor  if(file->encoding) heads["Content-Encoding"] = file->encoding;
abdff21999-12-27Martin Nilsson  if(!file->error)
41b77c1999-07-15David Hedbor  file->error=200;
abdff21999-12-27Martin Nilsson 
41b77c1999-07-15David Hedbor  if(file->expires)
7dba2f2000-05-08Martin Nilsson  heads->Expires = Roxen.http_date(file->expires);
41b77c1999-07-15David Hedbor 
a7ebda2000-01-30Per Hedbor  if(mappingp(file->extra_heads))
41b77c1999-07-15David Hedbor  heads |= file->extra_heads;
a7ebda2000-01-30Per Hedbor  if(mappingp(misc->moreheads))
41b77c1999-07-15David Hedbor  heads |= misc->moreheads; if(misc->range && file->len && objectp(file->file) && !file->data && file->error == 200 && (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
abdff21999-12-27Martin Nilsson  // use a wrapper and send a multi-part message.
41b77c1999-07-15David Hedbor  array ranges = parse_range_header(file->len); if(ranges) // No incorrect syntax...
abdff21999-12-27Martin Nilsson  {
41b77c1999-07-15David Hedbor  if(sizeof(ranges)) // And we have valid ranges as well.
d986281999-06-30David Hedbor  {
41b77c1999-07-15David Hedbor  file->error = 206; // 206 Partial Content if(sizeof(ranges) == 1) { heads["Content-Range"] = sprintf("bytes %d-%d/%d", @ranges[0], file->len); 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.
84ae131999-07-28David Hedbor  file->file = MultiRangeWrapper(file, heads, ranges, this_object());
41b77c1999-07-15David Hedbor  }
d986281999-06-30David Hedbor  } else {
41b77c1999-07-15David Hedbor  // Got the header, but the specified ranges was 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; }
d986281999-06-30David Hedbor  } } }
abdff21999-12-27Martin Nilsson 
41b77c1999-07-15David Hedbor  head_string = prot+" "+(file->rettext||errors[file->error])+"\r\n";
a7ebda2000-01-30Per Hedbor 
41b77c1999-07-15David Hedbor  foreach(indices(heads), h) if(arrayp(heads[h])) foreach(heads[h], tmp)
a7ebda2000-01-30Per Hedbor  head_string += h+": "+tmp+"\r\n";
41b77c1999-07-15David Hedbor  else
a7ebda2000-01-30Per Hedbor  head_string += h+": "+heads[h]+"\r\n";
abdff21999-12-27Martin Nilsson 
b908c02000-04-25Martin Stjernholm  if(file->len > -1) {
22dc8e1999-12-21Marcus Comstedt  head_string += "Content-Length: "+ file->len +"\r\n";
b908c02000-04-25Martin Stjernholm  if (file->len == 0) // Some browsers, e.g. Netscape 4.7, doesn't trust a zero // content length when using keep-alive. So let's force a // close in that case. misc->connection = "close"; }
49c07e2000-01-17Henrik Grubbström (Grubba)  else misc->connection = "close";
41b77c1999-07-15David Hedbor  head_string += "\r\n";
abdff21999-12-27Martin Nilsson 
41b77c1999-07-15David Hedbor  if(conf) conf->hsent += strlen(head_string); }
14179b1997-01-29Per Hedbor  }
6a409d1999-12-27Martin Nilsson  REQUEST_WERR(sprintf("Sending result for prot:%O, method:%O file:%O\n",
7a0ee61998-06-27Henrik Grubbström (Grubba)  prot, method, file));
bd22231998-03-27David Hedbor  MARK_FD("HTTP handled");
ebb1c51998-02-24Per Hedbor 
903e571998-03-25Per Hedbor #ifdef KEEP_ALIVE
ebb1c51998-02-24Per Hedbor  if(!leftovers) leftovers = data||"";
903e571998-03-25Per Hedbor #endif
ebb1c51998-02-24Per Hedbor 
18b3161998-03-25David Hedbor  if(method != "HEAD" && file->error != 304) // No data for these two...
b1fca01996-11-12Per Hedbor  {
f71a6b2000-01-22Martin Stjernholm  if(my_fd->query_fd && my_fd->query_fd() >= 0 && file->len > 0 && file->len < 2000) { // Ordinary connection, and a short file. // Just do a blocking write().
0b0d082000-02-14Per Hedbor  my_fd->write(head_string+ (file->file?file->file->read(file->len): (file->data[..file->len-1])));
f71a6b2000-01-22Martin Stjernholm  do_log(); return; }
0b0d082000-02-14Per Hedbor  if(head_string) send(head_string);
ebb1c51998-02-24Per Hedbor  if(file->data && strlen(file->data)) send(file->data, file->len);
abdff21999-12-27Martin Nilsson  if(file->file)
ebb1c51998-02-24Per Hedbor  send(file->file, file->len);
3582882000-03-24Per Hedbor  } else { if(head_string) { if(my_fd->query_fd && my_fd->query_fd() >= 0 && file->len > 0 && file->len < 2000) { // Ordinary connection, and a short file. // Just do a blocking write(). my_fd->write( head_string ); do_log( ); return; } send(head_string); }
18b3161998-03-25David Hedbor  file->len = 1; // Keep those alive, please...
3582882000-03-24Per Hedbor  }
336ee41999-04-17Henrik Grubbström (Grubba)  start_sender(do_log);
14179b1997-01-29Per Hedbor }
d943c81998-05-18Henrik Grubbström (Grubba)  // Execute the request void handle_request( ) {
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("HTTP: handle_request()");
9d8ea11999-04-16Henrik Grubbström (Grubba) 
d943c81998-05-18Henrik Grubbström (Grubba) #ifdef MAGIC_ERROR if(prestate->old_error) { array err = get_error(variables->error); if(err) { if(prestate->plain) { file = ([
2827392000-03-26Martin Stjernholm  "type":"text/plain",
f288181998-05-25Per Hedbor  "data":generate_bugreport( @err ),
d943c81998-05-18Henrik Grubbström (Grubba)  ]);
8a80d41999-05-20Per Hedbor  send_result(); return;
d943c81998-05-18Henrik Grubbström (Grubba)  } else { if(prestate->find_file) {
8641d42000-03-24Martin Stjernholm  if (!roxen.configuration_authenticate (this_object(), "View Settings"))
7dba2f2000-05-08Martin Nilsson  file = Roxen.http_auth_required("admin");
d943c81998-05-18Henrik Grubbström (Grubba)  else
8641d42000-03-24Martin Stjernholm  file = ([ "type":"text/html", "data":handle_error_file_request( @err ), ]);
8a80d41999-05-20Per Hedbor  send_result(); return;
d943c81998-05-18Henrik Grubbström (Grubba)  } } } } #endif /* MAGIC_ERROR */ remove_call_out(do_timeout); MARK_FD("HTTP handling request");
35c2631999-02-16Per Hedbor  array e;
c5e0961999-10-04Per Hedbor  if(e= catch(file = conf->handle_request( this_object() )))
2b40f41999-12-29Martin Stjernholm  INTERNAL_ERROR( e );
338ffa2000-02-15Martin Nilsson  if( !file || !file->pipe )
8139e82000-02-10Per Hedbor  send_result();
d943c81998-05-18Henrik Grubbström (Grubba) }
14179b1997-01-29Per Hedbor /* We got some data on a socket.
abdff21999-12-27Martin Nilsson  * =================================================
14179b1997-01-29Per Hedbor  */
ebb1c51998-02-24Per Hedbor int processed;
14179b1997-01-29Per Hedbor void got_data(mixed fooid, string s) {
acd34a2000-01-22Martin Stjernholm  if (mixed err = catch {
14179b1997-01-29Per Hedbor  int tmp;
abdff21999-12-27Martin Nilsson 
3235691998-03-26Per Hedbor  MARK_FD("HTTP got data");
ceb9271997-05-15David Hedbor  remove_call_out(do_timeout);
abdff21999-12-27Martin Nilsson  call_out(do_timeout, 30); // Close down if we don't get more data
ceb9271997-05-15David Hedbor  // within 30 seconds. Should be more than enough.
3d5e911998-05-21David Hedbor  time = _time(1); // Check is made towards this to make sure the object // is not killed prematurely.
c4ab8f1999-07-15David Hedbor  if(!raw) raw = s;
abdff21999-12-27Martin Nilsson  else
c4ab8f1999-07-15David Hedbor  raw += s;
14179b1997-01-29Per Hedbor  if(wanted_data) {
c4ab8f1999-07-15David Hedbor  if(strlen(s) + have_data < wanted_data)
14179b1997-01-29Per Hedbor  {
c4ab8f1999-07-15David Hedbor  // cache += ({ s });
14179b1997-01-29Per Hedbor  have_data += strlen(s);
9d8ea11999-04-16Henrik Grubbström (Grubba) 
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("HTTP: We want more data.");
14179b1997-01-29Per Hedbor  return; } }
abdff21999-12-27Martin Nilsson 
41b77c1999-07-15David Hedbor  // If the request starts with newlines, it's a broken request. Really! // sscanf(s, "%*[\n\r]%s", s);
c4ab8f1999-07-15David Hedbor  if(strlen(raw)) tmp = parse_got();
41b77c1999-07-15David Hedbor  switch(tmp)
abdff21999-12-27Martin Nilsson  {
fb5f681998-11-04Peter Bortas  case 0:
abdff21999-12-27Martin Nilsson  // if(this_object())
c4ab8f1999-07-15David Hedbor  // cache = ({ s }); // More on the way.
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("HTTP: Request needs more data.");
14179b1997-01-29Per Hedbor  return;
abdff21999-12-27Martin Nilsson 
14179b1997-01-29Per Hedbor  case 1:
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("HTTP: Stupid Client Error");
41b77c1999-07-15David Hedbor  end((prot||"HTTP/1.0")+" 500 Stupid Client Error\r\nContent-Length: 0\r\n\r\n");
14179b1997-01-29Per Hedbor  return; // Stupid request.
abdff21999-12-27Martin Nilsson 
14179b1997-01-29Per Hedbor  case 2:
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("HTTP: Done");
14179b1997-01-29Per Hedbor  end(); return; }
ebb1c51998-02-24Per Hedbor 
ed15161999-11-19Per Hedbor  mixed q; if( q = variables->magic_roxen_automatic_charset_variable )
7dba2f2000-05-08Martin Nilsson  decode_charset_encoding( Roxen.get_client_charset_decoder( q,this_object() ) );
653d7d2000-02-14Per Hedbor  if( input_charset ) decode_charset_encoding( input_charset );
ed15161999-11-19Per Hedbor 
653d7d2000-02-14Per Hedbor  if (misc->host) {
94a5b91999-10-25Henrik Grubbström (Grubba)  // FIXME: port_obj->name & port_obj->default_port are constant // consider caching them? conf = port_obj->find_configuration_for_url(port_obj->name + "://" + misc->host + (search(misc->host, ":")<0? (":"+port_obj->default_port):"") + not_query, this_object());
653d7d2000-02-14Per Hedbor  } else {
94a5b91999-10-25Henrik Grubbström (Grubba)  // No host header. // Fallback to using the first configuration bound to this port. conf = port_obj->urls[port_obj->sorted_urls[0]]->conf;
2e9a2b2000-03-10Henrik Grubbström (Grubba)  misc->defaulted = 1;
1c87882000-03-10Henrik Grubbström (Grubba)  // Support delayed loading in this case too. if (!conf->inited) { conf->enable_all_modules(); }
94a5b91999-10-25Henrik Grubbström (Grubba)  }
c5e0961999-10-04Per Hedbor 
abdff21999-12-27Martin Nilsson  if (rawauth)
14179b1997-01-29Per Hedbor  {
c5e0961999-10-04Per Hedbor  /* Need to authenticate with the configuration */ array(string) y = rawauth / " "; realauth = 0; auth = 0;
abdff21999-12-27Martin Nilsson  if (sizeof(y) >= 2)
c5e0961999-10-04Per Hedbor  { y[1] = MIME.decode_base64(y[1]); realauth = y[1];
abdff21999-12-27Martin Nilsson  if (conf->auth_module)
c5e0961999-10-04Per Hedbor  y = conf->auth_module->auth(y, this_object()); auth = y; } }
abdff21999-12-27Martin Nilsson  if( misc->proxyauth )
c5e0961999-10-04Per Hedbor  { /* Need to authenticate with the configuration */
abdff21999-12-27Martin Nilsson  if (sizeof(misc->proxyauth) >= 2)
c5e0961999-10-04Per Hedbor  {
703bf91999-10-04Marcus Comstedt  // misc->proxyauth[1] = MIME.decode_base64(misc->proxyauth[1]);
abdff21999-12-27Martin Nilsson  if (conf->auth_module)
703bf91999-10-04Marcus Comstedt  misc->proxyauth
c5e0961999-10-04Per Hedbor  = conf->auth_module->auth(misc->proxyauth,this_object() ); }
14179b1997-01-29Per Hedbor  }
2154ca1997-08-31Per Hedbor 
c5e0961999-10-04Per Hedbor  conf->received += strlen(s); conf->requests++;
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("HTTP: Calling roxen.handle().");
9d8ea11999-04-16Henrik Grubbström (Grubba) 
abdff21999-12-27Martin Nilsson  my_fd->set_close_callback(0); my_fd->set_read_callback(0);
ebb1c51998-02-24Per Hedbor  processed=1;
35c2631999-02-16Per Hedbor  roxen.handle(this_object()->handle_request);
acd34a2000-01-22Martin Stjernholm 
653d7d2000-02-14Per Hedbor  }) {
acd34a2000-01-22Martin Stjernholm  report_error("Internal server error: " + describe_backtrace(err)); my_fd->close(); destruct (my_fd); disconnect(); }
b1fca01996-11-12Per Hedbor }
abdff21999-12-27Martin Nilsson /* Get a somewhat identical copy of this object, used when doing
b1fca01996-11-12Per Hedbor  * 'simulated' requests. */ object clone_me() {
14179b1997-01-29Per Hedbor  object c,t;
e554ae1999-12-26Johan Sundström  c=object_program(t=this_object())(0, port_obj);
cbe6c62000-03-18Martin Stjernholm #ifdef ID_OBJ_DEBUG
8bf31a2000-03-20Martin Stjernholm  werror ("clone %O -> %O\n", t, c);
cbe6c62000-03-18Martin Stjernholm #endif
b1fca01996-11-12Per Hedbor 
ebb1c51998-02-24Per Hedbor // c->first = first;
0e5b1a1999-10-10Per Hedbor  c->port_obj = port_obj;
b1fca01996-11-12Per Hedbor  c->conf = conf; c->time = time;
f6d62d1997-03-26Per Hedbor  c->raw_url = raw_url; c->variables = copy_value(variables);
48fa361997-04-05Per Hedbor  c->misc = copy_value(misc); c->misc->orig = t;
f6d62d1997-03-26Per Hedbor 
b1fca01996-11-12Per Hedbor  c->prestate = prestate; c->supports = supports;
f6d62d1997-03-26Per Hedbor  c->config = config;
dc5f172000-04-28Martin Nilsson  c->client_var = client_var;
f6d62d1997-03-26Per Hedbor 
b1fca01996-11-12Per Hedbor  c->remoteaddr = remoteaddr;
f6d62d1997-03-26Per Hedbor  c->host = host;
b1fca01996-11-12Per Hedbor  c->client = client;
f6d62d1997-03-26Per Hedbor  c->referer = referer; c->pragma = pragma; c->cookies = cookies; c->my_fd = 0; c->prot = prot;
1afe371997-06-12Henrik Grubbström (Grubba)  c->clientprot = clientprot;
f6d62d1997-03-26Per Hedbor  c->method = method;
abdff21999-12-27Martin Nilsson  // realfile virtfile // Should not be copied.
f6d62d1997-03-26Per Hedbor  c->rest_query = rest_query; c->raw = raw; c->query = query; c->not_query = not_query; c->data = data;
746c3b1998-09-16Peter J. Holzer  c->extra_extension = extra_extension;
b1fca01996-11-12Per Hedbor  c->auth = auth; c->realauth = realauth;
f6d62d1997-03-26Per Hedbor  c->rawauth = rawauth; c->since = since;
b1fca01996-11-12Per Hedbor  return c; } void clean() {
4b71f51998-03-25David Hedbor  if(!(my_fd && objectp(my_fd))) end();
abdff21999-12-27Martin Nilsson  else if((_time(1) - time) > 4800)
3235691998-03-26Per Hedbor  end();
b1fca01996-11-12Per Hedbor }
d391422000-03-25Martin Stjernholm static void create(object f, object c)
b1fca01996-11-12Per Hedbor {
14179b1997-01-29Per Hedbor  if(f) {
41b77c1999-07-15David Hedbor  MARK_FD("HTTP connection"); f->set_nonblocking(got_data, 0, end);
14179b1997-01-29Per Hedbor  my_fd = f;
c5e0961999-10-04Per Hedbor  if( c )
ddefa61999-10-04Marcus Comstedt  port_obj = c;
3158d21997-12-10David Hedbor  call_out(do_timeout, 30);
3d5e911998-05-21David Hedbor  time = _time(1);
14179b1997-01-29Per Hedbor  }
b1fca01996-11-12Per Hedbor }
ceb9271997-05-15David Hedbor 
ebb1c51998-02-24Per Hedbor void chain(object f, object c, string le) { my_fd = f;
870a9e1999-10-04Per Hedbor  port_obj = c;
ebb1c51998-02-24Per Hedbor  do_not_disconnect=-1;
a7ebda2000-01-30Per Hedbor 
9211b21998-04-24Per Hedbor  MARK_FD("Kept alive");
4949d71998-03-28David Hedbor  if(strlen(le)) // More to handle already. got_data(0,le);
a7ebda2000-01-30Per Hedbor  else {
18c62d1999-05-27Stephen R. van den Berg  // If no pipelined data is available, call out...
d391422000-03-25Martin Stjernholm  remove_call_out(do_timeout);
18c62d1999-05-27Stephen R. van den Berg  call_out(do_timeout, 150); time = _time(1); }
4949d71998-03-28David Hedbor 
ebb1c51998-02-24Per Hedbor  if(!my_fd) { if(do_not_disconnect == -1) { do_not_disconnect=0; disconnect(); }
a7ebda2000-01-30Per Hedbor  } else {
abdff21999-12-27Martin Nilsson  if(do_not_disconnect == -1)
903e571998-03-25Per Hedbor  do_not_disconnect = 0;
a7ebda2000-01-30Per Hedbor  if(!processed) {
903e571998-03-25Per Hedbor  f->set_close_callback(end); f->set_read_callback(got_data); }
ebb1c51998-02-24Per Hedbor  } }
08bd3c1999-11-02Per Hedbor  string _sprintf( ) {
8bf31a2000-03-20Martin Stjernholm #ifdef ID_OBJ_DEBUG return "RequestID()" + (__marker ? "[" + __marker->count + "]" : ""); #else
08bd3c1999-11-02Per Hedbor  return "RequestID()";
8bf31a2000-03-20Martin Stjernholm #endif
08bd3c1999-11-02Per Hedbor }
84fb682000-02-03Per Hedbor  Stdio.File connection( ) { return my_fd; } Configuration configuration() { return conf; }