e3bda32000-01-12Martin Nilsson // This is a roxen protocol module.
199d031999-09-05Francesco Chemolli // Modified by Francesco Chemolli to add throttling capabilities.
f41b982009-05-07Martin Stjernholm // Copyright © 1996 - 2009, Roxen IS.
6a409d1999-12-27Martin Nilsson 
b243512010-10-18Martin Jonsson constant cvs_version = "$Id: http.pike,v 1.628 2010/10/18 07:42:38 marty Exp $";
a3ebea2000-08-12Per Hedbor // #define REQUEST_DEBUG
86e77d1998-05-07Per Hedbor #define MAGIC_ERROR
d7427c2006-04-20Henrik Grubbström (Grubba) #define REQUESTID this
86e77d1998-05-07Per Hedbor #ifdef MAGIC_ERROR inherit "highlight_pike"; #endif
6a409d1999-12-27Martin Nilsson 
2a2a5b1996-12-01Per Hedbor // HTTP protocol module.
1e6cb62008-11-05Martin Stjernholm #include <roxen.h>
9c19002001-02-27Per Hedbor #define TIMER_PREFIX "http:" #include <timers.h>
26f44e2000-08-13Per Hedbor 
3f6e4b2002-02-06Martin Stjernholm inherit RequestID;
ebb1c51998-02-24Per Hedbor #ifdef PROFILE
c5e0961999-10-04Per Hedbor #define HRTIME() gethrtime()
8afc811998-02-04Per Hedbor int req_time = HRTIME();
ebb1c51998-02-24Per Hedbor #endif
c5e0961999-10-04Per Hedbor 
aa92c11998-08-20Henrik Grubbström (Grubba) #ifdef REQUEST_DEBUG
41b77c1999-07-15David Hedbor int footime, bartime;
39202e2003-11-03Martin Stjernholm #define REQUEST_WERR(X) do {bartime = gethrtime()-footime; werror("%s (%d)\n", (X), bartime);footime=gethrtime();} while (0)
aa92c11998-08-20Henrik Grubbström (Grubba) #else
39202e2003-11-03Martin Stjernholm #define REQUEST_WERR(X) do {} while (0)
aa92c11998-08-20Henrik Grubbström (Grubba) #endif
3235691998-03-26Per Hedbor #ifdef FD_DEBUG
b303402004-08-11Henrik Grubbström (Grubba) #ifdef REQUEST_DEBUG #define FD_WERR(X) REQUEST_WERR(X) #else #define FD_WERR(X) werror("%s\n", (X)) #endif
39202e2003-11-03Martin Stjernholm #define MARK_FD(X) do { \
1139a12006-10-13Martin Stjernholm  int _fd = -1; \ if (my_fd->query_fd) \ _fd = my_fd->query_fd(); \ if (my_fd->query_stream) \ if (object stream = my_fd->query_stream()) /* SSL */ \ if (stream->query_fd) \ _fd = stream->query_fd(); \
b303402004-08-11Henrik Grubbström (Grubba)  FD_WERR("FD " + (_fd == -1 ? sprintf ("%O", my_fd) : _fd) + ": " + (X)); \
39202e2003-11-03Martin Stjernholm  mark_fd(_fd, (X)+" "+remoteaddr); \ } while (0)
3235691998-03-26Per Hedbor #else
39202e2003-11-03Martin Stjernholm #define MARK_FD(X) do {} while (0)
3235691998-03-26Per Hedbor #endif
1139a12006-10-13Martin Stjernholm #ifdef CONNECTION_DEBUG // Currently only used by CONNECTION_DEBUG to label each message with // the fd. private string debug_fd_str; private string debug_my_fd() { if (my_fd->query_fd) return debug_fd_str = (string) my_fd->query_fd(); if (my_fd->query_stream) if (object stream = my_fd->query_stream()) // SSL if (stream->query_fd) return debug_fd_str = (string) stream->query_fd(); return debug_fd_str = sprintf ("%O", my_fd); } #define DEBUG_GET_FD \ (my_fd ? debug_fd_str || debug_my_fd() : \ debug_fd_str ? "ex " + debug_fd_str : "??") #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;
b1fca01996-11-12Per Hedbor 
fc40392008-08-15Martin Stjernholm private array(string) cache; private int wanted_data, have_data; private object(String.Buffer) data_buffer;
7ee5651996-12-10Per Hedbor 
fc40392008-08-15Martin Stjernholm private multiset(string) none_match;
29abe62004-04-13Henrik Grubbström (Grubba) 
766c712003-02-03Henrik Grubbström (Grubba) int kept_alive;
5cf99d2005-11-18Henrik Grubbström (Grubba) #if defined(DEBUG) && defined(THREADS)
39202e2003-11-03Martin Stjernholm #define CHECK_FD_SAFE_USE do { \
4a98402004-08-18Martin Stjernholm  if (this_thread() != roxen->backend_thread && \
39202e2003-11-03Martin Stjernholm  (my_fd->query_read_callback() || my_fd->query_write_callback() || \ my_fd->query_close_callback() || \ !zero_type (find_call_out (do_timeout)))) \
b303402004-08-11Henrik Grubbström (Grubba)  error ("Got callbacks but not called from backend thread.\n" \
5cf99d2005-11-18Henrik Grubbström (Grubba)  "backend_thread:%O\n" \ "this thread:%O\n" \
b303402004-08-11Henrik Grubbström (Grubba)  "rcb:%O\n" \ "wcb:%O\n" \ "ccb:%O\n" \ "timeout:%O\n", \
5cf99d2005-11-18Henrik Grubbström (Grubba)  roxen->backend_thread, this_thread(), \
b303402004-08-11Henrik Grubbström (Grubba)  my_fd->query_read_callback(), \ my_fd->query_write_callback(), \ my_fd->query_close_callback(), \ find_call_out (do_timeout)); \
39202e2003-11-03Martin Stjernholm  } while (0) #else #define CHECK_FD_SAFE_USE do {} while (0) #endif
b1fca01996-11-12Per Hedbor #include <roxen.h> #include <module.h>
773ac62001-03-20Martin Stjernholm #include <request_trace.h>
ae79002008-11-05Martin Stjernholm #define MY_TRACE_ENTER(MSG) ID_TRACE_ENTER (this, (MSG), 0) #define MY_TRACE_LEAVE(MSG) ID_TRACE_LEAVE (this, (MSG))
b1fca01996-11-12Per Hedbor 
d5cf252002-12-11Anders Johansson mapping(string:array) real_variables = ([]);
5aca9b2001-09-28Martin Stjernholm mapping(string:mixed)|FakedVariables variables = FakedVariables( real_variables );
2fdf092000-12-02Per Hedbor 
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) ]);
99e65a2003-06-18Tomas Nilsson mapping (string:mixed) connection_misc = ([ ]);
8200cd1998-07-14Henrik Grubbström (Grubba) 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 = (< >);
ebb1c51998-02-24Per Hedbor multiset (string) pragma = (< >);
b1fca01996-11-12Per Hedbor 
beaca01998-02-20Per Hedbor mapping file;
b1fca01996-11-12Per Hedbor  string rest_query="";
26b0b92001-10-02Per Hedbor string raw="";
e885f72010-06-29Henrik Grubbström (Grubba) int raw_bytes = 0;
60a1a11998-08-03Henrik Grubbström (Grubba) string extra_extension = ""; // special hack for the language module
cf4a902001-01-19Per Hedbor 
fc40392008-08-15Martin Stjernholm protected mapping connection_stats = ([]);
b611c62002-03-27Per Hedbor 
cf4a902001-01-19Per Hedbor class AuthEmulator // Emulate the old (rather cumbersome) authentication API { mixed `[]( int i ) { User u; switch( i ) { case 0: return conf->authenticate( this_object() ); case 1: if( u = conf->authenticate( this_object() ) ) return u->name(); if( realauth ) return (realauth/":")[0]; case 2: if( u = conf->authenticate( this_object() ) ) return 0; if( realauth ) return ((realauth/":")[1..])*":"; } } int `!( ) { return !realauth; } }
9689f12002-09-03Martin Stjernholm array|AuthEmulator auth;
0b0d082000-02-14Per 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.
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
d5cf252002-12-11Anders Johansson mapping (string:mixed) throttle=([]);
ba88df1999-09-29Francesco Chemolli 
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; }
5f6dae2000-08-13Per Hedbor  if ( throttle->doit && conf->query("req_throttle") ) throttle->doit = 0; if( throttle->doit || conf->throttler ) pipe=roxen.slowpipe(); else pipe=roxen.fastpipe(); if (throttle->doit) { //we are sure that pipe is really a slowpipe. throttle->rate=max(throttle->rate, conf->query("req_throttle_min"));
ba88df1999-09-29Francesco Chemolli  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  }
b611c62002-03-27Per Hedbor  if( pipe->set_status_mapping ) pipe->set_status_mapping( connection_stats );
5f6dae2000-08-13Per Hedbor  if ( conf->throttler )
b611c62002-03-27Per Hedbor  pipe->assign_throttler( conf->throttler );
b1fca01996-11-12Per Hedbor }
199d031999-09-05Francesco Chemolli 
47a9402008-05-09Martin Stjernholm void send(string|object what, int|void len, #ifdef CONNECTION_DEBUG void|int connection_debug_verbose #endif )
b1fca01996-11-12Per Hedbor {
66d8452002-03-20Per Hedbor  if( len>0 && port_obj && port_obj->minimum_byterate )
dc6fe12001-08-20Per Hedbor  call_out( end, len / port_obj->minimum_byterate );
9c19002001-02-27Per Hedbor 
b1fca01996-11-12Per Hedbor  if(!what) return;
e5bad21998-02-10Per Hedbor  if(!pipe) setup_pipe();
1139a12006-10-13Martin Stjernholm  if(stringp(what)) {
acd7242004-04-27Martin Stjernholm #ifdef CONNECTION_DEBUG
47a9402008-05-09Martin Stjernholm  if (connection_debug_verbose
1e6cb62008-11-05Martin Stjernholm #if TOSTR(CONNECTION_DEBUG) != "1"
47a9402008-05-09Martin Stjernholm  // CONNECTION_DEBUG may be defined to something like "text/" // to see the response content for all content types with that // prefix, only headers are shown otherwise.
1e6cb62008-11-05Martin Stjernholm  || has_prefix(file->type || "", TOSTR(CONNECTION_DEBUG))
47a9402008-05-09Martin Stjernholm #endif ) werror ("HTTP[%s]: Response (length %d) ===============================\n"
7a14242009-01-07Martin Stjernholm  "%O\n", DEBUG_GET_FD, sizeof (what), what);
47a9402008-05-09Martin Stjernholm  else
1139a12006-10-13Martin Stjernholm  werror ("HTTP[%s]: Response =============================================\n"
47a9402008-05-09Martin Stjernholm  "String, length %d\n", DEBUG_GET_FD, sizeof (what));
acd7242004-04-27Martin Stjernholm #else
f136c22002-02-26Martin Stjernholm  REQUEST_WERR(sprintf("HTTP: Pipe string %O", what));
acd7242004-04-27Martin Stjernholm #endif
f136c22002-02-26Martin Stjernholm  pipe->write(what); } else {
acd7242004-04-27Martin Stjernholm #ifdef CONNECTION_DEBUG
1139a12006-10-13Martin Stjernholm  werror ("HTTP[%s]: Response =============================================\n" "Stream %O, length %O\n", DEBUG_GET_FD, what, len);
acd7242004-04-27Martin Stjernholm #else
f136c22002-02-26Martin Stjernholm  REQUEST_WERR(sprintf("HTTP: Pipe stream %O, length %O", what, len));
acd7242004-04-27Martin Stjernholm #endif
f136c22002-02-26Martin Stjernholm  pipe->input(what,len); }
b1fca01996-11-12Per Hedbor }
e7185f2005-04-01Henrik Grubbström (Grubba) int(0..1) my_fd_busy; int(0..1) pipe_pending;
5f6dae2000-08-13Per Hedbor void start_sender( )
9d8ea11999-04-16Henrik Grubbström (Grubba) {
e7185f2005-04-01Henrik Grubbström (Grubba)  if (my_fd_busy) { // We're waiting for the previous request to finish. pipe_pending = 1;
1139a12006-10-13Martin Stjernholm  REQUEST_WERR ("HTTP: Pipe pending.\n");
e7185f2005-04-01Henrik Grubbström (Grubba)  return; }
5f6dae2000-08-13Per Hedbor  if (pipe) {
f136c22002-02-26Martin Stjernholm  MARK_FD("HTTP really handled, piping response");
9d8ea11999-04-16Henrik Grubbström (Grubba) #ifdef FD_DEBUG
e2df6a2001-01-03Per Hedbor  call_out(timer, 30, predef::time(1)); // Update FD with time...
e29ec52004-08-17Henrik Grubbström (Grubba)  pipe->set_done_callback(lambda (int fsent) { remove_call_out(timer); do_log(fsent); } ); #else
5f6dae2000-08-13Per Hedbor  pipe->set_done_callback( do_log );
e29ec52004-08-17Henrik Grubbström (Grubba) #endif
5f6dae2000-08-13Per Hedbor  pipe->output( my_fd );
9d8ea11999-04-16Henrik Grubbström (Grubba)  } else { MARK_FD("HTTP really handled, pipe done");
5f6dae2000-08-13Per Hedbor  do_log();
9d8ea11999-04-16Henrik Grubbström (Grubba)  } }
e7185f2005-04-01Henrik Grubbström (Grubba) void my_fd_released() { my_fd_busy = 0;
1139a12006-10-13Martin Stjernholm  REQUEST_WERR ("HTTP: Fd released.\n");
e7185f2005-04-01Henrik Grubbström (Grubba)  if (pipe_pending) { start_sender(); } }
3e2c342001-01-11Per Hedbor #ifdef OLD_RXML_CONFIG private void really_set_config(array mod_config)
b1fca01996-11-12Per Hedbor {
3e2c342001-01-11Per Hedbor  string url; if(sscanf(replace(raw_url,({"%3c","%3e","%3C","%3E" }), ({"<",">","<",">"})),"/<%*s>/%s",url)!=2) url = "/";
e3bda32000-01-12Martin Nilsson  else
3e2c342001-01-11Per Hedbor  url = "/"+url;
e3bda32000-01-12Martin Nilsson 
3e2c342001-01-11Per Hedbor  multiset do_mod_config( multiset config )
b1fca01996-11-12Per Hedbor  {
3e2c342001-01-11Per Hedbor  if(!mod_config) return config; foreach(mod_config, string m) if(m[0]=='-') config[m[1..]]=0; else config[m]=1; return config; };
0ddff61997-07-20Henrik Grubbström (Grubba) 
3e2c342001-01-11Per Hedbor  void do_send_reply( string what, string url ) {
e7185f2005-04-01Henrik Grubbström (Grubba)  // FIXME: Delayed chaining! my_fd_busy.
39202e2003-11-03Martin Stjernholm  CHECK_FD_SAFE_USE;
fce66d2001-08-13Martin Stjernholm  url = url_base() + url[1..];
96cf672009-01-15Martin Stjernholm  string res = prot + " 302 Roxen 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"; #ifdef CONNECTION_DEBUG werror ("HTTP[%s]: Blocking response (length %d) ========================\n" "%O\n", DEBUG_GET_FD, sizeof (res), res); #else REQUEST_WERR(sprintf("HTTP: Send blocking %O", res)); #endif
dcc8952001-10-09Marcus Wellhardh  my_fd->set_blocking();
96cf672009-01-15Martin Stjernholm  my_fd->write (res);
3e2c342001-01-11Per Hedbor  my_fd->close(); my_fd = 0; end(); };
0ddff61997-07-20Henrik Grubbström (Grubba) 
3e2c342001-01-11Per Hedbor  if(supports->cookies) { do_send_reply("Set-Cookie: "+ Roxen.http_roxen_config_cookie(indices(do_mod_config(config))*","), url ); return;
b1fca01996-11-12Per Hedbor  }
3e2c342001-01-11Per Hedbor  if (sscanf(replace(url, ({ "%28", "%29" }), ({ "(", ")" })), "/(%*s)/%s", url) == 2) url = "/" + url; do_send_reply(0,Roxen.add_pre_state( url, do_mod_config( prestate ) ));
b1fca01996-11-12Per Hedbor }
a3ebea2000-08-12Per Hedbor #endif
b1fca01996-11-12Per Hedbor 
edc50f2005-12-07Henrik Grubbström (Grubba) #if 0
ad3d952004-12-01Stefan Wallström //! Parse cookie strings.
55533a2002-03-22Ian Delahorne //! //! @param contents
ad3d952004-12-01Stefan Wallström //! HTTP transport-encoded cookie header value or array with values.
55533a2002-03-22Ian Delahorne //! //! @returns //! Returns the resulting current cookie mapping.
edc50f2005-12-07Henrik Grubbström (Grubba) //!
970cc92006-04-20Henrik Grubbström (Grubba) //! @deprecated CookieJar //! //! @seealso
edc50f2005-12-07Henrik Grubbström (Grubba) //! Use @[CookieJar] instead.
ad3d952004-12-01Stefan Wallström mapping(string:string) parse_cookies( array|string contents )
55533a2002-03-22Ian Delahorne { if(!contents) return cookies; // misc->cookies += ({contents});
ad3d952004-12-01Stefan Wallström  array tmp = arrayp(contents) ? contents : ({ contents}); foreach(tmp, string cookieheader) { foreach(((cookieheader/";") - ({""})), string c) { string name, value; while(sizeof(c) && c[0]==' ') c=c[1..]; if(sscanf(c, "%s=%s", name, value) == 2) { value=http_decode_string(value); name=http_decode_string(name); cookies[ name ]=value;
55533a2002-03-22Ian Delahorne #ifdef OLD_RXML_CONFIG
ad3d952004-12-01Stefan Wallström  if( (name == "RoxenConfig") && strlen(value) ) config = mkmultiset( value/"," );
55533a2002-03-22Ian Delahorne #endif
ad3d952004-12-01Stefan Wallström  } }
55533a2002-03-22Ian Delahorne  } return cookies; }
edc50f2005-12-07Henrik Grubbström (Grubba) #endif
55533a2002-03-22Ian Delahorne 
dcc8952001-10-09Marcus Wellhardh int things_to_do_when_not_sending_from_cache( )
a86c3b2000-08-28Per Hedbor { #ifdef OLD_RXML_CONFIG array mod_config; int config_in_url; #endif
2e0b592008-01-08Martin Stjernholm  misc->cachekey = ProtocolCacheKey();
a2a5aa2000-08-31Per Hedbor  misc->_cachecallbacks = ({});
bf25432001-02-24Per Hedbor 
a432ad2008-01-10Martin Stjernholm  init_pref_languages();
edc50f2005-12-07Henrik Grubbström (Grubba)  init_cookies();
a86c3b2000-08-28Per Hedbor  #ifndef DISABLE_SUPPORTS
f163772000-08-28Per Hedbor  if( !supports )
a86c3b2000-08-28Per Hedbor  { if( !client ) { client = ({ "unknown" }); array s_and_v = find_supports_and_vars("", supports); supports = s_and_v[0]; client_var = s_and_v[1]; } else { if( !client_var->Fullname ) client_var->Fullname = "unknown"; client_var->fullname=lower_case(client_var->Fullname); 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]; } }
18c1e52008-02-18Martin Stjernholm 
a86c3b2000-08-28Per Hedbor  if ( client_var->charset && client_var->charset != "iso-8859-1" ) {
d7427c2006-04-20Henrik Grubbström (Grubba)  // FIXME: This code is suspect, and probably ought to be removed. NO_PROTO_CACHE(); // FIXME: Why?
c0480e2008-02-18Martin Stjernholm  // I guess the line above can be removed if we register a vary // dependency on User-Agent. /mast
1455922005-12-05Henrik Grubbström (Grubba) 
a86c3b2000-08-28Per Hedbor  set_output_charset( client_var->charset ); input_charset = client_var->charset; }
c0480e2008-02-18Martin Stjernholm  #else // DISABLE_SUPPORTS
a86c3b2000-08-28Per Hedbor  supports = (< "images", "gifinline", "forms", "mailto">); #endif
18c1e52008-02-18Martin Stjernholm 
c0480e2008-02-18Martin Stjernholm  { string f = raw_url; sscanf (f, "%s?%s", f, query); if (query) { int i = search (query, "roxen_magic_per_u=%25"); if (i >= 0 && (i == 0 || query[i - 1] == '&')) { // Broken Safari detected // (http://bugs.webkit.org/show_bug.cgi?id=6452, historically // http://bugzilla.opendarwin.org/show_bug.cgi?id=6452) // Assume that %u and %U won't occur naturally. REQUEST_WERR(sprintf("Broken http encoding detected. query=%O\n", query)); query = replace(query, ({ "%25u", "%25U" }), ({ "%u", "%U" })); REQUEST_WERR(sprintf("Repaired query=%O\n", query)); } }
655ae92009-03-21Martin Stjernholm  { mapping(string:array(string)) vars = misc->post_variables ? misc->post_variables + ([]) : ([]); if (query) split_query_vars (query, vars); if (sizeof (vars)) { decode_query: { if (input_charset) { if (mixed err = catch { f = decode_query_charset (_Roxen.http_decode_string (f), vars, input_charset); }) { report_debug ("Client %O sent query %O which failed to decode " "with its own charset %O: %s", client_var->fullname, raw_url, input_charset, describe_error (err)); input_charset = 0; } else break decode_query;
c0480e2008-02-18Martin Stjernholm  }
655ae92009-03-21Martin Stjernholm  f = decode_query_charset (_Roxen.http_decode_string (f), vars, "roxen-http-default");
c0480e2008-02-18Martin Stjernholm  }
9968202009-03-21Martin Stjernholm  if (array(string) rest = m_delete (real_variables, "")) rest_query = rest[0];
655ae92009-03-21Martin Stjernholm  if (input_charset && misc->post_variables) { if (query) { // Complicated to repopulate misc->post_variables with // decoded values. function(string:string) decoder = Roxen.get_decoder_for_client_charset (input_charset); mapping(string:array(string)) decoded_vars = ([]); foreach (misc->post_variables; string var; array(string) vals) { var = decoder (var); // We know the post_variables are first in the array values. decoded_vars[var] = real_variables[var][..sizeof (vals) - 1]; } misc->post_variables = decoded_vars; } else misc->post_variables = real_variables + ([]); // Ensure that the variable names in misc->post_parts and // misc->files are decoded too. if (misc->post_parts) { mapping(string:string) decoded_names = ([]); function(string:string) decoder = Roxen.get_decoder_for_client_charset (input_charset); mapping(string:array(MIME.Message)) decoded_post_parts = ([]); foreach (misc->post_parts; string var; array(MIME.Message) parts) { string decoded_var = decoder (var); decoded_names[var] = decoded_var; decoded_post_parts[decoded_var] = parts; } misc->post_parts = decoded_post_parts; if (misc->files) misc->files = map (misc->files, decoded_names); } }
fdd1152008-09-26Martin Stjernholm  }
d4505c2008-09-26Martin Stjernholm  else {
655ae92009-03-21Martin Stjernholm  // No variables to decode, only the path. Optimize // decode_query with "roxen-http-default" since we know // there's no magic_roxen_automatic_charset_variable.
d4505c2008-09-26Martin Stjernholm  f = http_decode_string (f); if (input_charset) { if (mixed err = catch { f = Roxen.get_decoder_for_client_charset (input_charset) (f); }) report_debug ("Client %O requested path %O which failed to decode " "with the input charset %O: %s", client_var->fullname, f, input_charset, describe_error (err)); } else catch (f = utf8_to_string (f));
c0480e2008-02-18Martin Stjernholm  }
655ae92009-03-21Martin Stjernholm  // Now after charset decode we can add the multipart/form-data // variables in case they were rfc 2388 correct. if (mapping(string:array(string)) ppv = m_delete (misc, "proper_post_vars")) { misc->post_variables = ppv; foreach (ppv; string var; array(string) val) { if (array(string) oldval = real_variables[var]) // Ensure POST variables come first, for consistency. real_variables[var] = val + oldval; else real_variables[var] = val; } }
c0480e2008-02-18Martin Stjernholm  } #if 0 // f is sent to Unix API's that take NUL-terminated strings... // This should really not be necessary. /mast if(search(f, "\0") != -1) sscanf(f, "%s\0", f); #endif if( strlen( f ) > 5 ) { string a; switch( f[1] ) { #ifdef OLD_RXML_CONFIG case '<': if (sscanf(f, "/<%s>/%s", a, f)==2) { config_in_url = 1; mod_config = (a/","); f = "/"+f; } #endif // intentional fall-through case '(': if(sscanf(f, "/(%s)/%s", a, f)==2) { prestate = (multiset)( a/","-({""}) ); f = "/"+f; } } } if (has_prefix (f, "/")) not_query = combine_path_unix ("/", f); else not_query = combine_path_unix ("/", f)[1..]; }
552ed02008-01-10Martin Stjernholm  { int i = search (client, "MSIE"); if (i < 0) supports->vary = 1; else if (++i < sizeof (client) && sscanf (client[i], "%d", int msie_major) == 1) { // Vary doesn't work in MSIE <= 6. // FIXME: Does it really work in MSIE 7? if (msie_major >= 7) supports->vary = 1; }
214e602006-08-11Henrik Grubbström (Grubba)  }
18c1e52008-02-18Martin Stjernholm 
f136c22002-02-26Martin Stjernholm  //REQUEST_WERR("HTTP: parse_got(): supports");
a86c3b2000-08-28Per Hedbor  if(!referer) referer = ({ });
18c1e52008-02-18Martin Stjernholm 
a86c3b2000-08-28Per Hedbor  if(misc->proxyauth) { // The Proxy-authorization header should be removed... So there. mixed tmp1,tmp2; foreach(tmp2 = (raw / "\n"), tmp1) { if(!search(lower_case(tmp1), "proxy-authorization:")) tmp2 -= ({tmp1}); } raw = tmp2 * "\n"; }
18c1e52008-02-18Martin Stjernholm 
a86c3b2000-08-28Per Hedbor #ifdef OLD_RXML_CONFIG if(config_in_url) {
f136c22002-02-26Martin Stjernholm  //REQUEST_WERR("HTTP: parse_got(): config_in_url");
e056e92000-10-19Per Hedbor  really_set_config( mod_config );
dcc8952001-10-09Marcus Wellhardh  return 1;
a86c3b2000-08-28Per Hedbor  } #endif
18c1e52008-02-18Martin Stjernholm 
79e7c42004-05-05Marcus Wellhardh  if(!supports->cookies && !sizeof(config))
a86c3b2000-08-28Per Hedbor  config = prestate;
49681e2006-12-14Henrik Grubbström (Grubba)  else { // FIXME: Improve protocol cachability.
bc0fa02001-03-08Per Hedbor  if( port_obj->set_cookie
a86c3b2000-08-28Per Hedbor  && !cookies->RoxenUserID && strlen(not_query) && not_query[0]=='/' && method!="PUT") {
49681e2006-12-14Henrik Grubbström (Grubba)  NO_PROTO_CACHE();
a86c3b2000-08-28Per Hedbor  if (!(port_obj->set_cookie_only_once &&
49681e2006-12-14Henrik Grubbström (Grubba)  cache_lookup("hosts_for_cookie",remoteaddr))) {
85979c2008-08-18 Erik Dahl  misc->moreheads = ([ "Set-Cookie":Roxen.http_roxen_id_cookie(), ]);
49681e2006-12-14Henrik Grubbström (Grubba)  }
a86c3b2000-08-28Per Hedbor  if (port_obj->set_cookie_only_once) cache_set("hosts_for_cookie",remoteaddr,1); }
49681e2006-12-14Henrik Grubbström (Grubba)  }
2fdf092000-12-02Per Hedbor }
d25fde2000-10-17Per Hedbor 
3e3f202009-06-03Henrik Grubbström (Grubba) //! @param fragment //! Some incremental data received from the client. //! //! @returns //! @int //! @value 0 //! More data needed. //! @value 1 //! Failure (not used). //! @value 2 //! Done and handled (not used). //! @value 3 //! Processing done. //! @endint private int got_chunk_fragment(string fragment) {
7e28e22009-06-05Henrik Grubbström (Grubba) #ifdef CONNECTION_DEBUG werror("HTTP[%s]: Fragment (length %d+%d/%d) ============================\n" "%O\n", DEBUG_GET_FD, sizeof(fragment), sizeof(misc->chunk_buf||""), misc->chunk_len, fragment); #else REQUEST_WERR(sprintf("HTTP: Got Fragment %O", fragment)); #endif
3e3f202009-06-03Henrik Grubbström (Grubba)  string buf = fragment; int chunk_offset; if (misc->chunk_len) { if (misc->chunk_len >= sizeof(fragment)) { data_buffer->add(fragment); misc->chunk_len -= sizeof(fragment); return 0; // More data needed (eg EOF marker). } else { data_buffer->add(fragment[..misc->chunk_len - 1]); chunk_offset = misc->chunk_len; misc->chunk_len = 0; } } else { buf = misc->chunk_buf + buf; } misc->chunk_buf = ""; while(misc->chunked == 1) { int chunk_data_offset; string chunk_extras;
7608442009-06-04Henrik Grubbström (Grubba)  int n = sscanf(buf, "%" + chunk_offset + "*s%x%s\r\n%n",
3e3f202009-06-03Henrik Grubbström (Grubba)  misc->chunk_len, chunk_extras, chunk_data_offset); if (n < 4) { misc->chunk_buf = buf[chunk_offset..]; misc->chunk_len = 0; return 0; // More data needed to parse the chunk length. }
7e28e22009-06-05Henrik Grubbström (Grubba) #ifdef CONNECTION_DEBUG werror("HTTP[%s]: Got chunk with %d bytes of data.\n", DEBUG_GET_FD, misc->chunk_len); #endif
3e3f202009-06-03Henrik Grubbström (Grubba)  // FIXME: Currently we ignore the chunk_extras. if (sizeof(buf) < chunk_data_offset + misc->chunk_len) { if (!data_buffer) { // The 16384 is some reasonable extra padding to // avoid having to realloc. data_buffer =
e885f72010-06-29Henrik Grubbström (Grubba)  String.Buffer(sizeof(data) + misc->chunk_len + 16384); if (sizeof(data)) data_buffer->add(data);
3e3f202009-06-03Henrik Grubbström (Grubba)  data = ""; } data_buffer->add(buf[chunk_data_offset..]); misc->chunk_len -= sizeof(buf) - chunk_data_offset; return 0; // More data needed for the chunk. } else if (misc->chunk_len) { string str = buf[chunk_data_offset..chunk_data_offset + misc->chunk_len - 1];
e885f72010-06-29Henrik Grubbström (Grubba)  if (sizeof(data) && !data_buffer) {
3e3f202009-06-03Henrik Grubbström (Grubba)  // The 16384 is some reasonable extra padding to // avoid having to realloc. data_buffer = String.Buffer(sizeof(data) + misc->chunk_len + 16384); data_buffer->add(data); data_buffer->add(str); data = ""; } if (data_buffer) { data_buffer->add(str); } else { data = str; } chunk_offset = chunk_data_offset + misc->chunk_len; } else { // End marker for chunks! if (data_buffer) { data = data_buffer->get(); data_buffer = 0; } chunk_offset = chunk_data_offset; misc->len = sizeof(data); misc->chunked = 2; break; } } // Entity headers... if (misc->chunked == 2) { if (!misc->chunked_hp) {
7e28e22009-06-05Henrik Grubbström (Grubba) #ifdef CONNECTION_DEBUG werror("HTTP[%s]: Parsing entity headers...\n", DEBUG_GET_FD); #endif
3e3f202009-06-03Henrik Grubbström (Grubba)  if (buf[chunk_offset..chunk_offset + 1] == "\r\n") { // Special case: No entity headers; done.
7e28e22009-06-05Henrik Grubbström (Grubba) #ifdef CONNECTION_DEBUG werror("HTTP[%s]: No entity headers.\n", DEBUG_GET_FD); #endif
3e3f202009-06-03Henrik Grubbström (Grubba)  leftovers = buf[chunk_offset+2..]; misc->chunked = 3; return 3; } misc->chunked_hp = Roxen.HeaderParser(); misc->chunked_hp->feed(" \r\n"); // Dummy first line... // Note: At least three spaces in the first line are needed // to avoid the HTTP/0.9 support in the header parser. } array res; if (mixed err = catch { res = misc->chunked_hp->feed(buf[chunk_offset..]); }) {
7e28e22009-06-05Henrik Grubbström (Grubba) #if defined(DEBUG) || defined(CONNECTION_DEBUG)
3e3f202009-06-03Henrik Grubbström (Grubba)  report_debug ("Got bad request (chunked), HeaderParser error: " + describe_error (err)); #endif // Assume no entity headers, and ensure that the connection won't be // reused. misc->connection = "close"; leftovers = ""; } else { if (!res) return 0;
7e28e22009-06-05Henrik Grubbström (Grubba) #ifdef CONNECTION_DEBUG werror("HTTP[%s]: Got entity headers: %O.\n", DEBUG_GET_FD, res[2]); #endif
3e3f202009-06-03Henrik Grubbström (Grubba)  leftovers = res[0]; request_headers |= res[2];
e885f72010-06-29Henrik Grubbström (Grubba)  // Note: None of the headers special-cased in parse_got() below are
3e3f202009-06-03Henrik Grubbström (Grubba)  // allowed as entity headers, so there's no need for extra // parsing here (as is done for the main headers there). } misc->chunked = 3; m_delete(misc, "chunked_hp"); }
7e28e22009-06-05Henrik Grubbström (Grubba) #ifdef CONNECTION_DEBUG werror("HTTP[%s]: Chunked parsing done.\n", DEBUG_GET_FD); #endif
3e3f202009-06-03Henrik Grubbström (Grubba)  return 3; }
fc40392008-08-15Martin Stjernholm protected Roxen.HeaderParser hp = Roxen.HeaderParser();
d25fde2000-10-17Per Hedbor 
3e3f202009-06-03Henrik Grubbström (Grubba) //! Parse some data received from the client. //! //! @param new_data //! Incremental data from the client. //! //! This function is called from @[got_data] when it has received //! some data to parse the request headers. //! //! @returns //! @int //! @value 0 //! Need more data. //! @value 1 //! Failure. //! @value 2 //! Done and handled. //! @value 3 //! Done. //! @endint
a3ebea2000-08-12Per Hedbor private int parse_got( string new_data )
b1fca01996-11-12Per Hedbor {
5a1a5e2008-02-18Martin Stjernholm  if (hp) {
49d4602008-02-15Martin Stjernholm  string line; TIMER_START(parse_got);
5a1a5e2008-02-18Martin Stjernholm  if (!hrtime) hrtime = gethrtime();
49d4602008-02-15Martin Stjernholm  { array res; if( mixed err = catch( res = hp->feed( new_data ) ) ) {
95e41e2003-10-28Martin Stjernholm #ifdef DEBUG
49d4602008-02-15Martin Stjernholm  report_debug ("Got bad request, HeaderParser error: " + describe_error (err));
95e41e2003-10-28Martin Stjernholm #endif
49d4602008-02-15Martin Stjernholm  return 1; } if( !res ) { TIMER_END(parse_got); return 0; // Not enough data } [data, line, request_headers] = res;
9c19002001-02-27Per Hedbor  }
49d4602008-02-15Martin Stjernholm  hp = 0; TIMER_END(parse_got);
a3ebea2000-08-12Per Hedbor 
49d4602008-02-15Martin Stjernholm  // The following was earlier a separate function parse_got_2.
6110fd2008-02-15Martin Stjernholm 
49d4602008-02-15Martin Stjernholm  TIMER_START(parse_got_2); TIMER_START(parse_got_2_parse_line);
6110fd2008-02-15Martin Stjernholm  string f; array(string) sl = line / " "; switch( sizeof( sl ) ) { default: sl = ({ sl[0], sl[1..sizeof(sl)-2]*" ", sl[-1] }); /* FALL_THROUGH */ case 3: /* HTTP/1.0 */ method = sl[0]; f = sl[1]; clientprot = sl[2]; prot = clientprot; if(!(< "HTTP/1.0", "HTTP/1.1" >)[prot]) { int maj,min; if( sscanf(prot, "HTTP/%d.%d", maj, min) == 2 ) // Comply with the annoying weirdness of RFC 2616. prot = "HTTP/" + maj + "." + min; else // We're nice here and assume HTTP even if the protocol // is something very weird. prot = "HTTP/1.1"; } // HTTP/1.1 and later default to keep-alive. misc->connection = "keep-alive"; if (prot == "HTTP/1.0") { // But HTTP/1.0 did not. misc->connection = "close"; } break;
c0202a2001-02-01Per Hedbor 
6110fd2008-02-15Martin Stjernholm  case 2: // HTTP/0.9 case 1: // PING misc->connection = "close"; method = sl[0]; f = sl[-1]; if( sizeof( sl ) == 1 ) sscanf( method, "%s%*[\r\n]", method );
c0202a2001-02-01Per Hedbor 
6110fd2008-02-15Martin Stjernholm  clientprot = prot = "HTTP/0.9"; if(method != "PING") method = "GET"; // 0.9 only supports get. else { // FIXME: my_fd_busy.
96cf672009-01-15Martin Stjernholm #ifdef CONNECTION_DEBUG werror ("HTTP[%s]: Response =============================================\n" "%O\n", DEBUG_GET_FD, "PONG\r\n"); #else REQUEST_WERR("HTTP: Send PONG"); #endif
6110fd2008-02-15Martin Stjernholm  my_fd->write("PONG\r\n"); TIMER_END(parse_got_2_parse_line); TIMER_END(parse_got_2); return 2; } data = ""; // no headers or extra data... sscanf( f, "%s%*[\r\n]", f ); if (sizeof(sl) == 1) NO_PROTO_CACHE(); break;
94add02001-02-01Henrik Grubbström (Grubba) 
6110fd2008-02-15Martin Stjernholm  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); //REQUEST_WERR(sprintf("HTTP: raw_url %O", raw_url));
7ec6601997-01-29Per Hedbor 
435dfc2008-02-18Martin Stjernholm  if(!remoteaddr) { if(my_fd) { remoteaddr = my_fd->query_address(); if(remoteaddr) sscanf(remoteaddr, "%s %*s", remoteaddr); } if(!remoteaddr) {
118fce2009-05-29Martin Stjernholm  REQUEST_WERR(sprintf ("HTTP: No remote address (error %d).", my_fd->errno()));
435dfc2008-02-18Martin Stjernholm  TIMER_END(parse_got_2); return 2; }
e5bad21998-02-10Per Hedbor  }
7ec6601997-01-29Per Hedbor 
435dfc2008-02-18Martin Stjernholm  TIMER_START(parse_got_2_parse_headers);
5a1a5e2008-02-18Martin Stjernholm  foreach (request_headers; string linename; array|string contents)
b1fca01996-11-12Per Hedbor  {
435dfc2008-02-18Martin Stjernholm  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;
3e3f202009-06-03Henrik Grubbström (Grubba)  case "transfer-encoding": misc->transfer_encoding = map(lower_case(contents)/";", String.trim_all_whites); if (has_value(misc->transfer_encoding, "chunked")) { misc->chunked = 1; misc->chunk_buf = "";
e885f72010-06-29Henrik Grubbström (Grubba)  new_data = data; // For got_chunk_fragment() below. data = "";
3e3f202009-06-03Henrik Grubbström (Grubba)  } break;
435dfc2008-02-18Martin Stjernholm  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; case "user-agent": if( !client ) { sscanf(contents, "%s via", contents); client_var->Fullname=contents; client = contents/" " - ({ "" }); } break; case "request-range": contents = lower_case(contents-" "); if (has_prefix(contents, "bytes")) // Only care about "byte" ranges. misc->range = contents[6..]; break; 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 "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":
34acd02008-02-19Martin Stjernholm  // The following is the useless result of an old "fix" (it // was lowercased before that); it'd been better to have used // the field directly in request_headers instead if the // lowercasing messed things up.
435dfc2008-02-18Martin Stjernholm  misc[linename] = contents;
34acd02008-02-19Martin Stjernholm #define WS "%*[ \t]" sscanf (contents, WS"%[^ \t/]"WS"/"WS"%[^ \t;]"WS";"WS"%s", string ct_type, string ct_subtype, misc->content_type_params); misc->content_type_type = lower_case (ct_type + "/" + ct_subtype);
435dfc2008-02-18Martin Stjernholm  break; case "destination": if (mixed err = catch { contents = http_decode_string (Standards.URI(contents)->path); }) {
dd37192004-04-21Henrik Grubbström (Grubba) #ifdef DEBUG
435dfc2008-02-18Martin Stjernholm  report_debug(sprintf("Destination header contained a bad URI: %O\n" "%s", contents, describe_error(err)));
dd37192004-04-21Henrik Grubbström (Grubba) #endif /* DEBUG */
435dfc2008-02-18Martin Stjernholm  } misc["new-uri"] = VFS.normalize_path (contents); break;
2750d42009-01-15Martin Stjernholm  case "expect": // RFC 2616, section 14.20: "A server that does not // understand or is unable to comply with any of the // expectation values in the Expect field of a request MUST // respond with appropriate error status." We only handle the // standard 100-continue case (see ready_to_receive).
11171a2009-04-01Thomas Gusenleitner  // Also: "Comparison of expectation values is case-insensitive // for unquoted tokens (including the 100-continue token), and // is case-sensitive for quoted-string expectation-extensions." if (String.trim_all_whites(lower_case(contents)) != "100-continue") {
96cf672009-01-15Martin Stjernholm  string res = "HTTP/1.1 417 Expectation Failed\r\n\r\n"; #ifdef CONNECTION_DEBUG werror ("HTTP[%s]: Response (length %d) =================================\n" "%O\n", DEBUG_GET_FD, sizeof (res), res); #else REQUEST_WERR(sprintf("HTTP: Send %O", res)); #endif my_fd->write (res);
2750d42009-01-15Martin Stjernholm  return 2; } break;
435dfc2008-02-18Martin Stjernholm  }
41b77c1999-07-15David Hedbor  }
435dfc2008-02-18Martin Stjernholm  TIMER_END(parse_got_2_parse_headers);
5a1a5e2008-02-18Martin Stjernholm  }
6110fd2008-02-15Martin Stjernholm 
1752c32003-04-22Dan Nelson  TIMER_START(parse_got_2_more_data);
3e3f202009-06-03Henrik Grubbström (Grubba)  if (misc->chunked) { int ret = got_chunk_fragment(new_data); if (ret != 3) { REQUEST_WERR(sprintf("HTTP: More data needed in %s (chunked).", method)); ready_to_receive(); TIMER_END(parse_got_2_more_data); TIMER_END(parse_got_2); return ret; } } else if(misc->len)
a86c3b2000-08-28Per Hedbor  { int l = misc->len; wanted_data=l; have_data=strlen(data);
fdd26d2000-07-23Stefan Wallström 
a86c3b2000-08-28Per Hedbor  if(strlen(data) < l) {
f136c22002-02-26Martin Stjernholm  REQUEST_WERR(sprintf("HTTP: More data needed in %s.", method));
e1995f2003-12-29Henrik Grubbström (Grubba)  ready_to_receive();
3d2ce82005-11-24Henrik Grubbström (Grubba)  TIMER_END(parse_got_2_more_data);
0f15122003-02-25Henrik Grubbström (Grubba)  TIMER_END(parse_got_2);
a86c3b2000-08-28Per Hedbor  return 0; } leftovers = data[l+2..]; data = data[..l+1];
3e3f202009-06-03Henrik Grubbström (Grubba)  } else { leftovers = data;
e885f72010-06-29Henrik Grubbström (Grubba)  data = "";
3e3f202009-06-03Henrik Grubbström (Grubba)  }
e885f72010-06-29Henrik Grubbström (Grubba)  if (sizeof(data)) {
1752c32003-04-22Dan Nelson  switch(method) { case "POST":
5fdddc2009-03-31Martin Stjernholm  // FIXME: Get this POST variable munching out of the backend thread!
18c1e52008-02-18Martin Stjernholm  // See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4
655ae92009-03-21Martin Stjernholm  // and rfc 2388 for specs of the format of form submissions.
34acd02008-02-19Martin Stjernholm  switch (misc->content_type_type)
faf7c02001-08-23Henrik Grubbström (Grubba)  {
34acd02008-02-19Martin Stjernholm  case "application/x-www-form-urlencoded": { // Form data.
18c1e52008-02-18Martin Stjernholm 
34acd02008-02-19Martin Stjernholm  // 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. // // 4.5 and older trimmed id->data directly. We no longer do // that to allow the application to see the unmodified data if // the client says it's application/x-www-form-urlencoded but // in reality sends something else (some versions of the roxen // applauncher did that). (This is safe for correct // applauncher/x-www-form-urlencoded data since it doesn't // contain any whitespace.)
655ae92009-03-21Martin Stjernholm  string v = String.trim_all_whites (data);
34acd02008-02-19Martin Stjernholm 
655ae92009-03-21Martin Stjernholm  // This will be charset decoded later in
c0480e2008-02-18Martin Stjernholm  // things_to_do_when_not_sending_from_cache.
655ae92009-03-21Martin Stjernholm  split_query_vars (v, misc->post_variables = ([]));
faf7c02001-08-23Henrik Grubbström (Grubba)  break;
18c1e52008-02-18Martin Stjernholm  }
a86c3b2000-08-28Per Hedbor 
18c1e52008-02-18Martin Stjernholm  case "multipart/form-data": {
eab87a2008-08-13Mathias Södermark  // Set Guess to one in order to fix the incorrect quoting of paths // from Internet Explorer when sending a file.
655ae92009-03-21Martin Stjernholm  MIME.Message messg = MIME.Message(data, request_headers, UNDEFINED, 1); array(MIME.Message) parts = messg->body_parts; if (!parts) {
d0739e2002-02-14Henrik Grubbström (Grubba)  report_error("HTTP: Bad multipart/form-data.\n" " headers:\n" "%{ %O:%O\n%}" " data:\n" "%{ %O\"\\n\"\n%}", (array)request_headers, data/"\n"); /* FIXME: Should this be reported to the client? */
655ae92009-03-21Martin Stjernholm  } else { // We got two cases here: On one hand we got what rfc 2388 says, and // on the other we got how essentially all browsers actually behave. // // The latter means that the text values and form variable names are // sent in some poorly defined encoding, which is best to treat like // the same sorry situation for url variables and // application/x-www-form-urlencoded. // // On the first hint that the data actually is correct according to // rfc 2388, we skip the bug compat charset handling. The hints we // look for are: // // o A Content-Disposition parameter which is rfc 2047 encoded. // o A Content-Type header which specifies a charset. { // Another difference is that multiple values for the same field // are simply included as multiple body parts by browsers, while // rfc 2388 mandates using one multipart/mixed entry. Let's // convert to the simpler de-facto form. array(MIME.Message) subparts = ({}); foreach (parts; int i; MIME.Message part) if (array(MIME.Message) sub = part->body_parts) { // Assume multipart/mixed - it should never be anything else. subparts += sub; parts[i] = 0; } if (sizeof (subparts)) parts = parts - ({0}) + subparts; // Note: Keeps order per field. } mapping(string:string) rfc2047_decoded = ([]); int rfc2388_mode; foreach (parts, MIME.Message part) { foreach (part->disp_params;; string param) { string decoded; if (!catch (decoded = MIME.decode_words_text_remapped (param)) && decoded != param) rfc2047_decoded[param] = decoded; } if (part->params->charset) rfc2388_mode = 1; } if (sizeof (rfc2047_decoded)) rfc2388_mode = 1; mapping(string:array(MIME.Message)) post_parts = misc->post_parts = ([]); if (rfc2388_mode) { // Will this be executed ever? // misc->proper_post_vars is temporary storage that is moved into // real_variables after charset decode in // things_to_do_when_not_sending_from_cache. mapping(string:array(string)) post_vars = misc->proper_post_vars = ([]); foreach (parts, MIME.Message part) { string name = part->disp_params->name; if (string decoded = rfc2047_decoded[name]) name = decoded; post_parts[name] += ({part}); string data = part->getdata(); if (string charset = part->param->charset) { if (mixed err = catch { data = Locale.Charset.decoder (charset)-> feed (data)->drain(); }) report_debug ("Client %q sent data for %q which failed to " "decode with supplied charset %q: %s", client_var->fullname, name, charset, describe_error (err)); } post_vars[name] += ({data}); if (part->headers["content-type"]) // Use type and subtype fields to avoid any extra parameters. post_vars[name + ".mimetype"] += ({part->type + "/" + part->subtype}); if (string filename = part->disp_params->filename) { if (string decoded = rfc2047_decoded[filename]) filename = decoded; post_vars[name + ".filename"] += ({filename}); misc->files += ({name}); }
12db5a2009-01-10Stephen R. van den Berg  }
655ae92009-03-21Martin Stjernholm  } else { mapping(string:array(string)) post_vars = misc->post_variables = ([]); foreach (parts, MIME.Message part) { string name = part->disp_params->name; post_parts[name] += ({part}); // Note: name still encoded here.
5fdddc2009-03-31Martin Stjernholm  // Buglet: The following split into xxx, xxx.mimetype and // xxx.filename doesn't work well if the same variable xxx is // used both for files and other variables in the same form. // That's a quite odd case, though.
655ae92009-03-21Martin Stjernholm  post_vars[name] += ({part->getdata()}); if (part->headers["content-type"]) // Use type and subtype fields to avoid any extra parameters. post_vars[name + ".mimetype"] += ({part->type + "/" + part->subtype}); if (string filename = part->disp_params->filename) { post_vars[name + ".filename"] += ({filename}); misc->files += ({name}); // Note: name still encoded here. }
12db5a2009-01-10Stephen R. van den Berg  }
d0739e2002-02-14Henrik Grubbström (Grubba)  }
faf7c02001-08-23Henrik Grubbström (Grubba)  } break; }
18c1e52008-02-18Martin Stjernholm  }
1752c32003-04-22Dan Nelson  break;
dde9651996-12-08David Hedbor  }
ceb9271997-05-15David Hedbor  }
1752c32003-04-22Dan Nelson  TIMER_END(parse_got_2_more_data);
6110fd2008-02-15Martin Stjernholm 
8e7d8c2001-11-07Henrik Grubbström (Grubba)  if (!(< "HTTP/1.0", "HTTP/0.9" >)[prot]) {
01f8b82001-11-05Henrik Grubbström (Grubba)  if (!misc->host) { // RFC 2616 requires this behaviour.
96cf672009-01-15Martin Stjernholm  string res = (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"; #ifdef CONNECTION_DEBUG werror ("HTTP[%s]: Response (length %d) =================================\n" "%O\n", DEBUG_GET_FD, sizeof (res), res); #else
01f8b82001-11-05Henrik Grubbström (Grubba)  REQUEST_WERR("HTTP: HTTP/1.1 request without a host header.");
96cf672009-01-15Martin Stjernholm #endif
e7185f2005-04-01Henrik Grubbström (Grubba)  // FIXME: my_fd_busy.
96cf672009-01-15Martin Stjernholm  my_fd->write (res);
1752c32003-04-22Dan Nelson  TIMER_END(parse_got_2);
01f8b82001-11-05Henrik Grubbström (Grubba)  return 2; } }
0f15122003-02-25Henrik Grubbström (Grubba)  TIMER_END(parse_got_2);
6110fd2008-02-15Martin Stjernholm 
41b77c1999-07-15David Hedbor  return 3; // Done.
b1fca01996-11-12Per Hedbor } void disconnect() {
ebb1c51998-02-24Per Hedbor  file = 0;
aa02072002-04-05Henrik Grubbström (Grubba)  conf && conf->connection_drop( this_object() );
93aa102003-11-17Martin Stjernholm  if (my_fd) { MARK_FD("HTTP closed"); CHECK_FD_SAFE_USE;
4a98402004-08-18Martin Stjernholm  if (mixed err = catch (my_fd->close())) { #ifdef DEBUG report_debug ("Failed to close http(s) connection: " +
347d852007-03-09Martin Stjernholm  describe_backtrace (err));
6e4d302004-05-18Martin Stjernholm #endif
4a98402004-08-18Martin Stjernholm  }
93aa102003-11-17Martin Stjernholm  my_fd = 0; }
9c19002001-02-27Per Hedbor  MERGE_TIMERS(conf);
14179b1997-01-29Per Hedbor  destruct();
b1fca01996-11-12Per Hedbor }
fc40392008-08-15Martin Stjernholm protected void cleanup_request_object()
b1fca01996-11-12Per Hedbor {
01f4392002-03-28Per Hedbor  if( conf ) conf->connection_drop( this_object() );
a8e2b32004-05-07Henrik Grubbström (Grubba)  xml_data = 0;
1050aa2004-02-20Martin Stjernholm } void end(int|void keepit) {
f2eaa32007-09-03Henrik Grubbström (Grubba)  if (my_fd) { CHECK_FD_SAFE_USE; }
1050aa2004-02-20Martin Stjernholm  cleanup_request_object();
1752c32003-04-22Dan Nelson 
0aee222000-08-15Martin Stjernholm  if(keepit
c60a402000-08-22Per Hedbor  && !file->raw
f9252e2004-05-07Henrik Grubbström (Grubba)  && misc->connection != "close"
01f4392002-03-28Per Hedbor  && my_fd
1050aa2004-02-20Martin Stjernholm  // Is this necessary now when this function no longer is called // from the close callback? /mast
01f4392002-03-28Per Hedbor  && !catch(my_fd->query_address()) )
ebb1c51998-02-24Per Hedbor  { // Now.. Transfer control to a new http-object. Reset all variables etc..
26f44e2000-08-13Per Hedbor  object o = object_program(this_object())(0, 0, 0);
ebb1c51998-02-24Per Hedbor  o->remoteaddr = remoteaddr;
755c342010-10-11Stefan Wallström  // Don't share these since proxies can send different user-agent headers // when multiplexing several clients into one connection //o->client = client; //o->supports = supports; //o->client_var = client_var;
ebb1c51998-02-24Per Hedbor  o->host = host;
26f44e2000-08-13Per Hedbor  o->conf = conf;
e7185f2005-04-01Henrik Grubbström (Grubba)  o->my_fd_busy = !!pipe; o->pipe = 0;
99e65a2003-06-18Tomas Nilsson  o->connection_misc = connection_misc;
766c712003-02-03Henrik Grubbström (Grubba)  o->kept_alive = kept_alive+1;
0aee222000-08-15Martin Stjernholm  object fd = my_fd; my_fd=0;
5f6dae2000-08-13Per Hedbor  pipe = 0;
e7185f2005-04-01Henrik Grubbström (Grubba)  chained_to = o;
4a98402004-08-18Martin Stjernholm  call_out (o->chain, 0, fd,port_obj,leftovers);
0aee222000-08-15Martin Stjernholm  disconnect();
ebb1c51998-02-24Per Hedbor  return; }
ae2d0e2002-11-18Henrik Grubbström (Grubba)  data_buffer = 0;
5f6dae2000-08-13Per Hedbor  pipe = 0;
0aee222000-08-15Martin Stjernholm  disconnect();
b1fca01996-11-12Per Hedbor }
fc40392008-08-15Martin Stjernholm protected void close_cb()
1050aa2004-02-20Martin Stjernholm {
20f9b92004-05-12Martin Stjernholm #ifdef CONNECTION_DEBUG
1139a12006-10-13Martin Stjernholm  werror ("HTTP[%s]: Client close -----------------------------------------\n", DEBUG_GET_FD);
20f9b92004-05-12Martin Stjernholm #else
1050aa2004-02-20Martin Stjernholm  REQUEST_WERR ("HTTP: Got remote close.");
20f9b92004-05-12Martin Stjernholm #endif
1050aa2004-02-20Martin Stjernholm  CHECK_FD_SAFE_USE; cleanup_request_object(); data_buffer = 0; pipe = 0;
347d852007-03-09Martin Stjernholm  // Zero my_fd to avoid that the fd is closed by disconnect() - the // write direction might still want to use it. We rely on refcount // garbing instead, which means we must be careful about // deinstalling callbacks (they might not point to this object in // all cases).
5c21cb2006-11-16Martin Stjernholm  //
347d852007-03-09Martin Stjernholm  // http_fallback.ssl_alert_callback also counts on that my_fd // doesn't get closed since it calls this close callback and then // passes the connection on to fallback_redirect_request. my_fd->set_read_callback (0); my_fd->set_close_callback (0); if (my_fd->set_alert_callback) { // Ugly hack in case of an ssl connection: Zero the alert and // accept callbacks too. They are set by the http_fallback in // prot_https.pike. It normally ensures that they get zeroed too // after a successful connect or http redirect, but if the client // drops the connection very early, before either path has been // taken, we get here directly. (http_fallback can't wrap close_cb // since it's installed afterwards.) my_fd->set_alert_callback (0); my_fd->set_accept_callback (0); }
1050aa2004-02-20Martin Stjernholm  my_fd = 0; disconnect(); }
fc40392008-08-15Martin Stjernholm protected void do_timeout()
b1fca01996-11-12Per Hedbor {
e2df6a2001-01-03Per Hedbor  int elapsed = predef::time(1)-time;
9211b21998-04-24Per Hedbor  if(time && elapsed >= 30)
8afc811998-02-04Per Hedbor  {
b692e02004-08-11Henrik Grubbström (Grubba) #ifdef CONNECTION_DEBUG
1139a12006-10-13Martin Stjernholm  werror ("HTTP[%s]: Connection timed out. Closing.\n", DEBUG_GET_FD); #endif
b692e02004-08-11Henrik Grubbström (Grubba)  REQUEST_WERR(sprintf("HTTP: Connection timed out. Closing.\n" "rcb:%O\n" "wcb:%O\n" "ccb:%O\n", my_fd->query_read_callback(), my_fd->query_write_callback(), my_fd->query_close_callback()));
3235691998-03-26Per Hedbor  MARK_FD("HTTP timeout");
5f6dae2000-08-13Per Hedbor  end();
8afc811998-02-04Per Hedbor  } else {
39202e2003-11-03Martin Stjernholm #ifdef DEBUG error ("This shouldn't happen.\n"); #endif
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 }
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>";
4f6d922000-03-27Johan Sundström  return ("<a href=\"/(old_error,find_file)/error/?"+
3befe12000-08-22Martin Stjernholm  "file="+Roxen.http_encode_url(file)+ (fun ? "&fun="+Roxen.http_encode_url(fun) : "") +
8641d42000-03-24Martin Stjernholm  "&off="+qq+ "&error="+eid+
d8947f2002-07-03Henrik Grubbström (Grubba)  "&error_md5="+get_err_md5(get_err_info(eid))+
8641d42000-03-24Martin Stjernholm  (line ? "&line="+line+"#here" : "") + "\">");
86e77d1998-05-07Per Hedbor }
fc40392008-08-15Martin Stjernholm protected string error_page(string title, void|string msg, void|string longmsg, void|string body)
8641d42000-03-24Martin Stjernholm {
6cacf62006-03-10Martin Stjernholm  if (longmsg && has_suffix (longmsg, "\n")) longmsg = longmsg[..sizeof (longmsg) - 2];
7d7d1b2005-01-19Martin Stjernholm  return #"\ <html><head> <title>Internal Server Error</title> <style> .msg { font-family: verdana, helvetica, arial, sans-serif; font-size: 12px; line-height: 160% } .big { font-family: georgia, times, serif; font-size: 18px;
6cacf62006-03-10Martin Stjernholm  padding-top: 6px }
7d7d1b2005-01-19Martin Stjernholm  .info { font-family: verdana, helvetica, arial, sans-serif; font-size: 10px; color: #999999 } .list { padding-left: 20px; list-style-type:square; } .code { font-family: monaco, courier, monospace; font-size: 10px; color: #404070; } </style> </head> <body text='#000000' style='margin: 0; padding: 0' vlink='#2331d1' rightmargin='0' leftmargin='0' alink='#f6f6ff' link='#0000ee' bgcolor='#f2f1eb' bottommargin='0' topmargin='0'>
6cacf62006-03-10Martin Stjernholm <table border='0' cellspacing='30' cellpadding='0' height='99%'>
7d7d1b2005-01-19Martin Stjernholm  <tr>
6cacf62006-03-10Martin Stjernholm  <td width='1'><img src='/internal-roxen-500' /></td>
7d7d1b2005-01-19Martin Stjernholm  <td valign='bottom'><img src='/internal-roxen-server-error' /></td>
6cacf62006-03-10Martin Stjernholm  </tr> <tr> <td></td>
7d7d1b2005-01-19Martin Stjernholm  <td>
6cacf62006-03-10Martin Stjernholm  <div class='msg'>" + title + #"</div>" + (msg ? #" <div class='big'>" + msg + #"</div>" : "") + (longmsg ? #" <div class='code'><pre>" + longmsg + #"</pre></div>" : "") + #"
7d7d1b2005-01-19Martin Stjernholm  </td>
6cacf62006-03-10Martin Stjernholm  </tr> <tr> <td colspan='2'>" + (body ? #" <div class='msg'>" + body + #"</div>" : "") + #"
7d7d1b2005-01-19Martin Stjernholm  </td>
6cacf62006-03-10Martin Stjernholm  </tr> <tr valign='bottom' height='100%'> <td colspan='2'>
7d7d1b2005-01-19Martin Stjernholm  <table border='0' cellspacing='0' cellpadding='0'> <tr> <td><img src='/internal-roxen-roxen-mini.gif' /></td> <td class='info'> &nbsp;&nbsp;<b>" + roxen_product_name + #"</b> <font color='#ffbe00'>|</font> " + roxen_dist_version + #" </td> </tr> </table> </td> </tr> </table> </body></html>";
8641d42000-03-24Martin Stjernholm }
86e77d1998-05-07Per Hedbor 
8986cf2004-06-21Jonas Wallden 
fc40392008-08-15Martin Stjernholm protected string get_err_md5(array(string|array(string)|array(array)) err_info)
2b9d4b1998-03-25Per Hedbor {
d8947f2002-07-03Henrik Grubbström (Grubba)  if (err_info) {
4e32072009-01-09Stephen R. van den Berg  return String.string2hex(Crypto.MD5.hash(err_info[3]));
d8947f2002-07-03Henrik Grubbström (Grubba)  } return "NONE"; }
fc40392008-08-15Martin Stjernholm protected array(string|array(string)|array(array)) get_err_info(int eid, string|void md5)
d8947f2002-07-03Henrik Grubbström (Grubba) { array(string|array(string)|array(array)) err_info =
8641d42000-03-24Martin Stjernholm  roxen.query_var ("errors")[eid];
d8947f2002-07-03Henrik Grubbström (Grubba)  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) {
6cacf62006-03-10Martin Stjernholm  return error_page("Unregistered error");
d8947f2002-07-03Henrik Grubbström (Grubba)  } [string msg, array(string) rxml_bt, array(array) bt, string raw_bt_descr, string raw_url, string raw] = err_info;
8641d42000-03-24Martin Stjernholm 
6cacf62006-03-10Martin Stjernholm  sscanf (msg, "%s\n%s", string title, string longmsg);
dcf62c2006-05-04Martin Stjernholm  if (!title) title = msg;
8986cf2004-06-21Jonas Wallden  string body = "";
8641d42000-03-24Martin Stjernholm  if (rxml_bt && sizeof (rxml_bt)) {
8986cf2004-06-21Jonas Wallden  body += "RXML frame backtrace" "<ul class='list'>"; foreach(rxml_bt, string line) body += "<li class='code'>" + Roxen.html_encode_string(line) + "</li>\n"; body += "</ul>\n";
8641d42000-03-24Martin Stjernholm  }
c658b92000-03-24Martin Stjernholm  if (bt && sizeof (bt)) {
8986cf2004-06-21Jonas Wallden  body += "Pike backtrace" "<ul class='list'>";
8641d42000-03-24Martin Stjernholm  int q = sizeof (bt);
664da62001-06-08Martin Stjernholm  foreach(bt, [string file, int line, string func, string descr])
0efa772001-09-05Marcus Comstedt  { #if constant(PIKE_MODULE_RELOC) file = file && master()->relocate_module(file); #endif
8986cf2004-06-21Jonas Wallden  q--; body += "<li>" + link_to(file, line, func, eid, q) + // inserts <a> (file ? Roxen.html_encode_string(file) : "<i>Unknown program</i>") +
8641d42000-03-24Martin Stjernholm  (line ? ":" + line : "") +
8986cf2004-06-21Jonas Wallden  "</a>" + (file ? Roxen.html_encode_string(get_cvs_id(file)) : "") + "<br /><span class='code'>" +
e921302004-06-21Jonas Wallden  replace(Roxen.html_encode_string(descr), " ", "&nbsp;") +
8986cf2004-06-21Jonas Wallden  "</span></li>\n";
0efa772001-09-05Marcus Comstedt  }
8986cf2004-06-21Jonas Wallden  body += "</ul>\n\n";
2b9d4b1998-03-25Per Hedbor  }
8641d42000-03-24Martin Stjernholm 
8986cf2004-06-21Jonas Wallden  body += "<p>Generate " "<a href=\"/(old_error,plain)/error/?" "error=" + eid + "&error_md5=" + get_err_md5(get_err_info(eid)) + "\">" "text-only version</a> of this error message for bug reports.</p>";
6cacf62006-03-10Martin Stjernholm  return error_page("The server failed to fulfill your query.", title, longmsg != "" && longmsg, body);
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 {
a7d90b2004-04-25Henrik Grubbström (Grubba)  return ("Roxen version: "+roxen.version()+ (roxen.real_version != roxen.version()?
2827392000-03-26Martin Stjernholm  " ("+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 
1d31342000-09-03Per Hedbor int store_error(mixed _err)
86e77d1998-05-07Per Hedbor {
1d31342000-09-03Per Hedbor  mixed err = _err; _err = 0; // hide in backtrace, they are bad enough anyway...
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]; } }
1d31342000-09-03Per Hedbor  function dp = master()->describe_program;
008c692001-03-16Martin Nilsson 
8641d42000-03-24Martin Stjernholm  string cwd = getcwd() + "/"; array bt;
c658b92000-03-24Martin Stjernholm  if (arrayp (err) && sizeof (err) >= 2 && arrayp (err[1]) || objectp (err) && err->is_generic_error) {
008c692001-03-16Martin Nilsson  object d = master()->Describer();
664da62001-06-08Martin Stjernholm  d->identify_parts(err[1]);
008c692001-03-16Martin Nilsson  function dcl = d->describe_comma_list;
8641d42000-03-24Martin Stjernholm  bt = ({});
008c692001-03-16Martin Nilsson 
664da62001-06-08Martin Stjernholm  foreach (reverse (err[1]), mixed ent) {
8641d42000-03-24Martin Stjernholm  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])) {
3befe12000-08-22Martin Stjernholm  func = ""; if (object o = function_object (ent[2])) { string s; if (!catch (s = sprintf ("%O",o)) && s != "object") func = s + "->"; } func += function_name(ent[2]);
8641d42000-03-24Martin Stjernholm  if (!file) catch {
1d31342000-09-03Per Hedbor  file = dp(object_program( function_object( ent[2] ) ) );
8641d42000-03-24Martin Stjernholm  if (file[..sizeof (cwd) - 1] == cwd) file = file[sizeof (cwd)..]; }; } else if (stringp(ent[2])) func = ent[2];
2c639f2000-09-02Per Hedbor  else func ="<unknown function>";
8641d42000-03-24Martin Stjernholm  if (sizeof (ent) >= 4)
aceff22009-01-09Martin Stjernholm  descr = func + "(" +dcl(ent[3..],1000)+")";
8641d42000-03-24Martin Stjernholm  else descr = func + "()"; } else if (stringp (ent)) descr = ent; else if (catch (descr = sprintf ("%O", ent))) descr = "???"; bt += ({({file, line, func, descr})}); } }
3d77732001-12-04Martin Stjernholm  add_cvs_ids (err);
2827392000-03-26Martin Stjernholm  e[id] = ({msg,rxml_bt,bt,describe_backtrace (err),raw_url,censor(raw)});
86e77d1998-05-07Per Hedbor  return id; }
d8947f2002-07-03Henrik Grubbström (Grubba) array get_error(string eid, string md5)
86e77d1998-05-07Per Hedbor {
35c2631999-02-16Per Hedbor  mapping e = roxen.query_var("errors");
d8947f2002-07-03Henrik Grubbström (Grubba)  if(e) { array r = e[(int)eid];
4e32072009-01-09Stephen R. van den Berg  if (r && (md5 == String.string2hex(Crypto.MD5.hash(r[3])))) {
d8947f2002-07-03Henrik Grubbström (Grubba)  return r; } }
86e77d1998-05-07Per Hedbor  return 0; }
1d31342000-09-03Per Hedbor void internal_error(array _err)
b1fca01996-11-12Per Hedbor {
d7427c2006-04-20Henrik Grubbström (Grubba)  NO_PROTO_CACHE();
1d31342000-09-03Per Hedbor  mixed err = _err; _err = 0; // hide in backtrace, they are bad enough anyway...
bd22231998-03-27David Hedbor  array err2;
e9d1172001-07-31Per Hedbor  if(port_obj && 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));
8986cf2004-06-21Jonas Wallden  file = Roxen.http_low_answer(500, error_page("The server failed to fulfill " "your query due to an internal " "error in the internal error "
6cacf62006-03-10Martin Stjernholm  "routine."));
2b9d4b1998-03-25Per Hedbor  }
f788481998-03-18Henrik Grubbström (Grubba)  } else {
8986cf2004-06-21Jonas Wallden  file = Roxen.http_low_answer(500, error_page("The server failed to fulfill "
6cacf62006-03-10Martin Stjernholm  "your query."));
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.
48fa852004-01-19Martin Stjernholm #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)
2b40f41999-12-29Martin Stjernholm 
23a7891996-12-15Per Hedbor int wants_more() { return !!cache; }
fc40392008-08-15Martin Stjernholm protected object(this_program) chained_to;
e7185f2005-04-01Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm protected void destroy()
e7185f2005-04-01Henrik Grubbström (Grubba) {
34dd572007-02-26Martin Stjernholm  // To avoid references to destructed RequestID objects. Happens // otherwise when prot_https makes a http -> https redirect, for // instance. remove_call_out (do_timeout);
e7185f2005-04-01Henrik Grubbström (Grubba)  if (chained_to) {
d412542005-04-06Henrik Grubbström (Grubba)  // This happens when do_log() is called before the request // has been chained (eg for short data over fast connections).
e7185f2005-04-01Henrik Grubbström (Grubba)  call_out(chained_to->my_fd_released, 0); chained_to = 0; } }
8886652000-09-19Per Hedbor void do_log( int|void fsent )
d7b0871997-08-31Per Hedbor {
acd7242004-04-27Martin Stjernholm #ifdef CONNECTION_DEBUG
1139a12006-10-13Martin Stjernholm  werror ("HTTP[%s]: Response sent ========================================\n", DEBUG_GET_FD);
acd7242004-04-27Martin Stjernholm #endif
3235691998-03-26Per Hedbor  MARK_FD("HTTP logging"); // fd can be closed here
e7185f2005-04-01Henrik Grubbström (Grubba)  if (chained_to) { // Release the other sender. call_out(chained_to->my_fd_released, 0); chained_to = 0; }
9c19002001-02-27Per Hedbor  TIMER_START(do_log);
d7b0871997-08-31Per Hedbor  if(conf) {
f2eaa32007-09-03Henrik Grubbström (Grubba)  if(!(file->len = fsent) && pipe) {
5f6dae2000-08-13Per Hedbor  file->len = pipe->bytes_sent();
f2eaa32007-09-03Henrik Grubbström (Grubba)  }
d7b0871997-08-31Per Hedbor  if(conf) { if(file->len > 0) conf->sent+=file->len;
d986281999-06-30David Hedbor  file->len += misc->_log_cheat_addition;
9e03692010-05-19Fredrik Noring  { float t = (gethrtime() - hrtime)/1E6; if (t > 0.01) conf->request_num_runs_001s++; if (t > 0.05) conf->request_num_runs_005s++; if (t > 0.15) conf->request_num_runs_015s++; if (t > 0.50) conf->request_num_runs_05s++; if (t > 1.00) conf->request_num_runs_1s++; if (t > 5.00) conf->request_num_runs_5s++; if (t > 15.00) conf->request_num_runs_15s++; conf->request_acc_time += (int)(1E6*t); } { float t = handle_time/1E6; if (t > 0.01) conf->handle_num_runs_001s++; if (t > 0.05) conf->handle_num_runs_005s++; if (t > 0.15) conf->handle_num_runs_015s++; if (t > 0.50) conf->handle_num_runs_05s++; if (t > 1.00) conf->handle_num_runs_1s++; if (t > 5.00) conf->handle_num_runs_5s++; if (t > 15.00) conf->handle_num_runs_15s++; conf->handle_acc_time += (int)(1E6*t); } { float t = queue_time/1E6; if (t > 0.01) conf->queue_num_runs_001s++; if (t > 0.05) conf->queue_num_runs_005s++; if (t > 0.15) conf->queue_num_runs_015s++; if (t > 0.50) conf->queue_num_runs_05s++; if (t > 1.00) conf->queue_num_runs_1s++; if (t > 5.00) conf->queue_num_runs_5s++; if (t > 15.00) conf->queue_num_runs_15s++; conf->queue_acc_time += (int)(1E6*t); }
f7d9811997-09-12Per Hedbor  conf->log(file, this_object());
d7b0871997-08-31Per Hedbor  } }
35233d2000-09-01Per Hedbor  if( !port_obj ) {
9c19002001-02-27Per Hedbor  TIMER_END(do_log); MERGE_TIMERS(conf);
b611c62002-03-27Per Hedbor  if( conf ) conf->connection_drop( this_object() );
93aa102003-11-17Martin Stjernholm  call_out (disconnect, 0);
35233d2000-09-01Per Hedbor  return; }
9c19002001-02-27Per Hedbor  TIMER_END(do_log);
c60a402000-08-22Per Hedbor  end(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?)
f136c22002-02-26Martin Stjernholm  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,
e2df6a2001-01-03Per Hedbor  predef::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 {
b07b5e2004-06-21Henrik Grubbström (Grubba)  // Check that the file is valid and is present in the backtrace. string data; foreach(bt, array frame) { if (frame[0] == variables->file) { data = Stdio.read_bytes(variables->file); break; } }
8641d42000-03-24Martin Stjernholm  if(!data)
6cacf62006-03-10Martin Stjernholm  return error_page("Source file could not be read:", variables->file);
b07b5e2004-06-21Henrik Grubbström (Grubba) 
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;";
8986cf2004-06-21Jonas Wallden  lines[off] = indent + "<font size='+1'><b>"+down+code+"</a></b></font>";
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 
6cacf62006-03-10Martin Stjernholm  return error_page("Source code for", variables->file, 0,
8986cf2004-06-21Jonas Wallden  "<span class='code'><pre>" + (lines * "\n") + "</pre></span>");
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 = "";
496c842005-11-25Henrik Grubbström (Grubba)  void create(mapping _file, array _ranges, array(string)|string t, object id)
d986281999-06-30David Hedbor  { file = _file->file; len = _file->len;
a5c6e92005-11-25Henrik Grubbström (Grubba)  if (arrayp(t)) type = t[-1]; else type = t;
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",
a5c6e92005-11-25Henrik Grubbström (Grubba)  type, @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.
2ad0fd2006-11-08Henrik Grubbström (Grubba)  // werror("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]; } } }
9c19002001-02-27Per Hedbor // Parse the range header into multiple ranges.
d986281999-06-30David Hedbor 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; }
feb6802006-12-07Stefan Wallström  ranges += ({ ({ r1, 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; }
496c842005-11-25Henrik Grubbström (Grubba)  // Handle byte ranges. // NOTE: Modifies both arguments destructively. void handle_byte_ranges(mapping(string:mixed) file, mapping(string:array(string)|string) variant_heads) { if(misc->range && file->len && (method == "GET") && (file->error == 200) && (objectp(file->file) || file->data)) // 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 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... {
d7427c2006-04-20Henrik Grubbström (Grubba)  NO_PROTO_CACHE();
496c842005-11-25Henrik Grubbström (Grubba)  if(sizeof(ranges)) // And we have valid ranges as well. { m_delete(variant_heads, "Content-Length"); file->error = 206; // 206 Partial Content if(sizeof(ranges) == 1) { variant_heads["Content-Range"] = sprintf("bytes %d-%d/%d", @ranges[0], file->len); if (objectp(file->file)) { file->file->seek(ranges[0][0]); } else { file->data = file->data[ranges[0][0]..ranges[0][1]]; } 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. array(string)|string content_type = variant_heads["Content-Type"] || "application/octet-stream"; if(request_headers["request-range"]) { // Compat with old Netscape. variant_heads["Content-Type"] = "multipart/x-byteranges; boundary=" BOUND; } else { variant_heads["Content-Type"] = "multipart/byteranges; boundary=" BOUND; } if (objectp(file->file)) { // We do this by replacing the file object with a wrapper. // Nice and handy. file->file = MultiRangeWrapper(file, ranges, content_type, this_object()); } else { array(string) res = allocate(sizeof(ranges)*3+1); mapping(string:string) part_heads = ([ "Content-Type":content_type, ]); int j; foreach(ranges; int i; array(int) range) { res[j++] = "\r\n--" BOUND "\r\n"; part_heads["Content-Range"] = sprintf("bytes %d-%d/%d", @range, file->len);
f1a1532008-12-09Stefan Wallström  //res[j++] = Roxen.make_http_headers(part_heads);
6ac15f2008-12-09Stefan Wallström  // It seems like Acrobat/IE can freeze if the content-range header precedes the content-type header
f1a1532008-12-09Stefan Wallström  res[j++] = "Content-Type: "+part_heads["Content-Type"] + "\r\n" + "Content-Range: "+part_heads["Content-Range"] + "\r\n\r\n";
2ad0fd2006-11-08Henrik Grubbström (Grubba)  res[j++] = file->data[range[0]..range[1]];
496c842005-11-25Henrik Grubbström (Grubba)  }
feb6802006-12-07Stefan Wallström  res[j++] = "\r\n--" BOUND "--\r\n";
496c842005-11-25Henrik Grubbström (Grubba)  file->len = sizeof(file->data = res * ""); } }
214e602006-08-11Henrik Grubbström (Grubba)  variant_heads["Content-Length"] = (string)file->len;
496c842005-11-25Henrik Grubbström (Grubba)  } else { // Got the header, but the specified ranges were out of bounds. // Reply with a 416 Requested Range not satisfiable. file->error = 416; variant_heads["Content-Range"] = "*/"+file->len;
ed058f2010-05-26Henrik Grubbström (Grubba)  file->file = file->type = file->len = 0; file->data = "";
496c842005-11-25Henrik Grubbström (Grubba)  } } } }
45b22b1998-10-26Henrik Grubbström (Grubba) // Tell the client that it can start sending some more data void ready_to_receive() {
e1995f2003-12-29Henrik Grubbström (Grubba)  // FIXME: Only send once?
11171a2009-04-01Thomas Gusenleitner  if (clientprot == "HTTP/1.1" && stringp(request_headers->expect) && (String.trim_all_whites(lower_case(request_headers->expect)) == "100-continue") && !my_fd_busy) {
96cf672009-01-15Martin Stjernholm  string res = "HTTP/1.1 100 Continue\r\n\r\n"; #ifdef CONNECTION_DEBUG werror ("HTTP[%s]: Response (length %d) =================================\n" "%O\n", DEBUG_GET_FD, sizeof (res), res); #else REQUEST_WERR(sprintf("HTTP: Send %O", res)); #endif my_fd->write (res); }
45b22b1998-10-26Henrik Grubbström (Grubba) }
aa92c11998-08-20Henrik Grubbström (Grubba) 
3d2ce82005-11-24Henrik Grubbström (Grubba) // Send and account the formatted result void low_send_result(string headers, string data, int|void len, Stdio.File|void file) {
7a2e7c2007-08-14Martin Stjernholm 
f2eaa32007-09-03Henrik Grubbström (Grubba)  if (!my_fd) { do_log(0); return; }
4e40cc2010-06-30Henrik Grubbström (Grubba)  // Note: data may be UNDEFINED. if (!data) data = "";
e885f72010-06-29Henrik Grubbström (Grubba)  if (String.width (data) != 8) {
7a2e7c2007-08-14Martin Stjernholm  int from, to; foreach (data; from; int chr) if (chr > 255) break; to = from + 1024; from -= 1024; error ("Attempt to send wide data: %s%O%s\n" "Response mapping: %O\n" "output_charset: %O\n", from > 0 ? "..." : "", data[from..to], to < sizeof (data) ? "..." : "", (this_program::file->data ? this_program::file | (["data": "..."]) : this_program::file), output_charset); }
3d2ce82005-11-24Henrik Grubbström (Grubba)  conf->hsent += sizeof(headers); if(!kept_alive && (len > 0) && ((sizeof(headers) + len) < (HTTP_BLOCKING_SIZE_THRESHOLD))) {
ae79002008-11-05Martin Stjernholm  MY_TRACE_ENTER(sprintf("Sending blocking %d bytes of headers and " "%d bytes of string data",
e885f72010-06-29Henrik Grubbström (Grubba)  sizeof(headers), sizeof(data)));
3d2ce82005-11-24Henrik Grubbström (Grubba)  TIMER_START(blocking_write);
e885f72010-06-29Henrik Grubbström (Grubba)  if (sizeof(data) != len) {
3d2ce82005-11-24Henrik Grubbström (Grubba)  data = data[..len-1]; } if (file) { data = file->read(len);
e885f72010-06-29Henrik Grubbström (Grubba)  }
3d2ce82005-11-24Henrik Grubbström (Grubba) #ifdef CONNECTION_DEBUG
96cf672009-01-15Martin Stjernholm  werror("HTTP[%s]: Blocking response (length %d) ======================\n"
7a14242009-01-07Martin Stjernholm  "%O\n", DEBUG_GET_FD, sizeof (headers) + sizeof (data), headers + data);
3d2ce82005-11-24Henrik Grubbström (Grubba) #else REQUEST_WERR(sprintf("HTTP: Send blocking %O", headers + data)); #endif int s = my_fd->write(({ headers, data })); TIMER_END(blocking_write);
ae79002008-11-05Martin Stjernholm  MY_TRACE_LEAVE(sprintf("Blocking write wrote %d bytes", s));
3d2ce82005-11-24Henrik Grubbström (Grubba)  do_log(s); } else {
ae79002008-11-05Martin Stjernholm  MY_TRACE_ENTER(sprintf("Sending async %d bytes of headers and " "%d bytes of string data",
e885f72010-06-29Henrik Grubbström (Grubba)  sizeof(headers), sizeof(data)));
47a9402008-05-09Martin Stjernholm #ifndef CONNECTION_DEBUG
a5c6e92005-11-25Henrik Grubbström (Grubba)  REQUEST_WERR(sprintf("HTTP: Send headers %O", headers)); #endif
3d2ce82005-11-24Henrik Grubbström (Grubba)  if (sizeof(headers))
47a9402008-05-09Martin Stjernholm  send(headers, 0, #ifdef CONNECTION_DEBUG 1 #endif );
e885f72010-06-29Henrik Grubbström (Grubba)  if (sizeof(data))
3d2ce82005-11-24Henrik Grubbström (Grubba)  send(data, len); if (file) send(file, len);
ae79002008-11-05Martin Stjernholm  // MY_TRACE_LEAVE before start_sender since it might destruct us. MY_TRACE_LEAVE ("Async sender started");
3d2ce82005-11-24Henrik Grubbström (Grubba)  start_sender(); } }
56f6952009-01-28Martin Jonsson #ifdef HTTP_COMPRESSION
c993712010-09-02Martin Jonsson #ifdef HTTP_COMPR_STREAM class GzStreamingFile { //! Class that will compress a file on the fly, i.e. read() will //! return compressed data from the source file fed to @[create] or //! @[open]. Writing is not supported currently. protected Gz.deflate def; protected Stdio.File srcfile; protected string read_buf; protected int crc; protected int file_pos; protected int at_eof; protected int src_at_eof; protected int trailer_written; protected int level; constant default_level = 7; constant read_chunk_size = 1024*128; void create (void|Stdio.File file, void|int level) //! If the argument @[file] is provided, @[open] will be called with //! that file as argument. @[level] is the compression level. { if (level) this_program::level = level; else this_program::level = default_level; if (file) open (file); } int open (Stdio.File file) //! Opens the stream for reading with @[file] as source. { read_buf = make_header(); file_pos = 0; srcfile = file; crc = Gz.crc32(""); at_eof = 0; src_at_eof = 0; trailer_written = 0; def = Gz.deflate (-level, Gz.DEFAULT_STRATEGY); return 1; } string read (int len) { String.Buffer buf = String.Buffer(); while (sizeof (read_buf) + sizeof (buf) < len && !src_at_eof) { string chunk = srcfile->read (read_chunk_size); if (sizeof (chunk) < read_chunk_size) src_at_eof = 1; buf->add (def->deflate (chunk, Gz.NO_FLUSH)); crc = Gz.crc32 (chunk, crc); } if (src_at_eof && !trailer_written) { buf->add (def->deflate ("", Gz.FINISH)); buf->add (sprintf("%-4c%-4c", crc, srcfile->tell())); trailer_written = 1; } read_buf += buf->get(); string result = read_buf[..len-1]; read_buf = read_buf[sizeof(result)..]; file_pos += sizeof (result); if (src_at_eof && !sizeof (read_buf)) at_eof = 1; return result; } int tell() { return file_pos; } int eof() { return at_eof; } int seek (int pos) //! Note: the source file won't be seeked automatically. { open (srcfile); int offset; while (offset + read_chunk_size < pos) offset += sizeof (read (read_chunk_size)); read (pos-offset); return file_pos; } string make_header() { return sprintf("%1c%1c%1c%1c%4c%1c%1c", 0x1f, 0x8b, 8, 0, 0, 0, 3);
ff7e932010-10-08Martin Jonsson  } string _sprintf() { return sprintf ("GzStreamingFile (%O)", srcfile); }
c993712010-09-02Martin Jonsson } #endif
01d9cb2010-05-19Arjan van Staalduijnen private int(0..1) compression_enabled_for_mimetype (string mimetype) { mapping(string:int) compress_exact_mimetypes = conf->http_compr_exact_mimes; mapping(string:int) compress_main_mimetypes = conf->http_compr_main_mimes;
9016a82010-05-25Martin Jonsson  if (mimetype) // We are not interested in the charset spec. mimetype = (mimetype / ";")[0];
01d9cb2010-05-19Arjan van Staalduijnen  return (conf->http_compr_enabled && mimetype && (compress_exact_mimetypes[mimetype] || (sscanf (mimetype, "%[^/]", string main_type) && compress_main_mimetypes[main_type]))); }
56f6952009-01-28Martin Jonsson private string gzip_data(string data) {
0b78152009-01-29Stephen R. van den Berg  Stdio.FakeFile f = Stdio.FakeFile("", "wb");
56f6952009-01-28Martin Jonsson  // Reuse the Gz.File object to reduce the overhead of instantiating // Gz.deflate objects etc.
37820a2010-03-26Martin Jonsson  Gz.File gzfile = conf->gz_file_pool->get();
56f6952009-01-28Martin Jonsson  if(!gzfile) {
0b78152009-01-29Stephen R. van den Berg  gzfile = Gz.File(f, "wb");
56f6952009-01-28Martin Jonsson  gzfile->setparams(conf->query("http_compression_level"), Gz.DEFAULT_STRATEGY);
37820a2010-03-26Martin Jonsson  conf->gz_file_pool->set(gzfile);
56f6952009-01-28Martin Jonsson  } else {
0b78152009-01-29Stephen R. van den Berg  gzfile->open(f, "wb");
56f6952009-01-28Martin Jonsson  } gzfile->write(data); gzfile->close(); return (string)f; } private string gunzip_data(string data) {
0b78152009-01-29Stephen R. van den Berg  Stdio.FakeFile f = Stdio.FakeFile(data, "rb");
56f6952009-01-28Martin Jonsson 
37820a2010-03-26Martin Jonsson  Gz.File gzfile = conf->gz_file_pool->get();
56f6952009-01-28Martin Jonsson  if(!gzfile) {
0b78152009-01-29Stephen R. van den Berg  gzfile = Gz.File(f, "rb");
56f6952009-01-28Martin Jonsson  gzfile->setparams(conf->query("http_compression_level"), Gz.DEFAULT_STRATEGY);
37820a2010-03-26Martin Jonsson  conf->gz_file_pool->set(gzfile);
56f6952009-01-28Martin Jonsson  } else {
0b78152009-01-29Stephen R. van den Berg  gzfile->open(f, "rb");
56f6952009-01-28Martin Jonsson  } string res = gzfile->read(); gzfile->close(); return res; }
c993712010-09-02Martin Jonsson private #ifdef HTTP_COMPR_STREAM GzStreamingFile| #endif string try_gzip_data(Stdio.File|string data, string mimetype)
56f6952009-01-28Martin Jonsson { int min_data_length = conf->http_compr_minlen; int max_data_length = conf->http_compr_maxlen; int len = sizeof(data);
c993712010-09-02Martin Jonsson  if (compression_enabled_for_mimetype (mimetype)) { if (stringp (data)) { if (len >= min_data_length && (!max_data_length || len <= max_data_length)) { data = gzip_data(data); if (len>sizeof(data)) return data; } } #ifdef HTTP_COMPR_STREAM
ff7e932010-10-08Martin Jonsson  else if (objectp (data) && functionp (data->read) && functionp (data->tell)) {
c993712010-09-02Martin Jonsson  return GzStreamingFile (data, conf->query ("http_compression_level")); } #endif
56f6952009-01-28Martin Jonsson  } return 0; } private int(0..1) compress_dynamic_requests() { return conf->http_compr_dynamic_reqs; } private int(0..1) client_gzip_enabled() {
b243512010-10-18Martin Jonsson  array(string)|string accept_encoding = request_headers["accept-encoding"]; if (stringp (accept_encoding)) { return has_value("," + lower_case(accept_encoding - " " - "\t") + ",", ",gzip,"); } else if (arrayp (accept_encoding)) { foreach (accept_encoding, string ae) { if (has_value("," + lower_case(ae - " " - "\t") + ",", ",gzip,")) return 1; } }
c46b8e2009-01-28Jonas Wallden  return 0;
56f6952009-01-28Martin Jonsson } #endif // HTTP_COMPRESSION
d943c81998-05-18Henrik Grubbström (Grubba) // Send the result. void send_result(mapping|void result)
b1fca01996-11-12Per Hedbor {
9c19002001-02-27Per Hedbor  TIMER_START(send_result);
f2eaa32007-09-03Henrik Grubbström (Grubba)  if (my_fd) CHECK_FD_SAFE_USE;
39202e2003-11-03Martin Stjernholm 
3045db2009-09-21Martin Stjernholm  destruct_threadbound_session_objects(); handle_time = gethrtime() - handle_time; #if constant(System.CPU_TIME_IS_THREAD_LOCAL) handle_vtime = gethrvtime() - handle_vtime; #endif
d943c81998-05-18Henrik Grubbström (Grubba)  array err;
26f44e2000-08-13Per Hedbor  if (result)
d943c81998-05-18Henrik Grubbström (Grubba)  file = result;
0d0e952000-11-13Per Hedbor #ifdef PROFILE
2923b62004-05-19Henrik Grubbström (Grubba)  int elapsed = HRTIME()-req_time;
0d0e952000-11-13Per Hedbor  string nid = #ifdef FILE_PROFILE (raw_url/"?")[0] #else dirname((raw_url/"?")[0]) #endif
2923b62004-05-19Henrik Grubbström (Grubba)  + "?method="+method;
0d0e952000-11-13Per Hedbor  array p;
2923b62004-05-19Henrik Grubbström (Grubba)  if(!(p=conf->profile_map[nid])) { // ({ count, sum, max }) p = conf->profile_map[nid] = ({0, 0, 0}); }
0d0e952000-11-13Per Hedbor  p[0]++; p[1] += elapsed; if(elapsed > p[2]) p[2]=elapsed; #endif
e578562002-12-18Henrik Grubbström (Grubba)  REQUEST_WERR(sprintf("HTTP: response: prot %O, method %O, file %O, misc: %O", prot, method, file, misc));
abdff21999-12-27Martin Nilsson 
be97162003-11-25Anders Johansson #ifdef DEBUG_CACHEABLE
d894e82004-02-03Anders Johansson  report_debug("<=== Request for %s returned cacheable %d (proto cache %s).\n", raw_url, misc->cacheable, misc->no_proto_cache ? "disabled" : "enabled");
be97162003-11-25Anders Johansson #endif
d7427c2006-04-20Henrik Grubbström (Grubba)  if( prot == "HTTP/0.9" ) NO_PROTO_CACHE();
5f6dae2000-08-13Per Hedbor 
99b98b2000-08-14Per Hedbor  if(!mappingp(file))
b1fca01996-11-12Per Hedbor  {
d7427c2006-04-20Henrik Grubbström (Grubba)  NO_PROTO_CACHE();
99b98b2000-08-14Per Hedbor  if(misc->error_code)
1f57322008-05-07Martin Stjernholm  file = Roxen.http_status(misc->error_code, Roxen.http_status_messages[misc->error_code]);
99b98b2000-08-14Per Hedbor  else if(err = catch {
73ce3d2000-12-11Per Hedbor  file = conf->error_file( this_object() );
99b98b2000-08-14Per Hedbor  })
2b40f41999-12-29Martin Stjernholm  INTERNAL_ERROR(err);
99b98b2000-08-14Per Hedbor  }
9b0f912004-04-20Martin Stjernholm  else { if((file->file == -1) || file->leave_me)
b1fca01996-11-12Per Hedbor  {
9b0f912004-04-20Martin Stjernholm  TIMER_END(send_result); file = 0; pipe = 0;
b017232007-06-12Henrik Grubbström (Grubba)  if (do_not_disconnect) return;
9b0f912004-04-20Martin Stjernholm  my_fd = 0; return;
5f6dae2000-08-13Per Hedbor  }
abdff21999-12-27Martin Nilsson 
9b0f912004-04-20Martin Stjernholm  if(file->type == "raw") file->raw = 1; }
c1413f2005-10-28Henrik Grubbström (Grubba)  // Invariant part of header. (Cached)
27ef7f2005-11-18Henrik Grubbström (Grubba)  // Contains the result line except for the protocol and code parts.
c1413f2005-10-28Henrik Grubbström (Grubba)  // Note: Only a single CRLF as terminator. string head_string=""; // Variant part of header. // Currently the Date and Connection headers. // Note: Terminated with a double CRLF. string variant_string="";
27ef7f2005-11-18Henrik Grubbström (Grubba)  // The full header block (prot + " " + code + head_string + variant_string).
c1413f2005-10-28Henrik Grubbström (Grubba)  string full_headers="";
a5c6e92005-11-25Henrik Grubbström (Grubba) #if 0 REQUEST_WERR(sprintf("HTTP: Sending result for prot:%O, method:%O, file:%O", prot, method, file)); #endif
29abe62004-04-13Henrik Grubbström (Grubba)  if(!file->raw && (prot != "HTTP/0.9")) {
a5c6e92005-11-25Henrik Grubbström (Grubba)  if (!sizeof (file) && multi_status) file = multi_status->http_answer();
57de552004-05-05Martin Stjernholm 
a5c6e92005-11-25Henrik Grubbström (Grubba)  if (file->error == Protocols.HTTP.HTTP_NO_CONTENT) { file->len = 0; file->data = ""; }
edaa6f2001-02-01Per Hedbor 
a5c6e92005-11-25Henrik Grubbström (Grubba)  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";
29abe62004-04-13Henrik Grubbström (Grubba)  }
a5c6e92005-11-25Henrik Grubbström (Grubba)  if (has_value (head_status, "\n")) // Fold lines nicely. head_status = map (head_status / "\n", String.trim_all_whites) * " "; }
c2d7aa2004-04-13Martin Stjernholm 
cf81c82009-01-29Martin Jonsson #ifdef HTTP_COMPRESSION
c0af642010-05-26Martin Jonsson  if(compression_enabled_for_mimetype (get_response_content_type (file))) {
cf81c82009-01-29Martin Jonsson  // Notify proxies etc. that we depend on the Accept-Encoding header, // but we don't want to register it as a vary callback since it's // handled directly by the protocol cache. if(!misc->vary)
e0bafe2009-04-17Martin Jonsson  misc->vary = (< "accept-encoding" >);
cf81c82009-01-29Martin Jonsson  else
e0bafe2009-04-17Martin Jonsson  misc->vary["accept-encoding"] = 1;
cf81c82009-01-29Martin Jonsson  } #endif
a5c6e92005-11-25Henrik Grubbström (Grubba)  mapping(string:string) heads = make_response_headers (file);
7fceff2005-11-28Henrik Grubbström (Grubba)  // Notes about the variant headers: // // Date Changes with every request. // Content-Type May change if a byte-range request is performed. // Content-Length May change due to If-* headers, etc. // Connection Depends on the protocol version and state.
3344142008-05-13Martin Stjernholm  // Expires Might need to modify this for HTTP/1.0 clients. // Cache-Control Might need to modify this when sending stale responses.
a5c6e92005-11-25Henrik Grubbström (Grubba)  mapping(string:string) variant_heads = ([ "Date":"", "Content-Type":"", "Content-Length":"",
94e9ac2005-12-05Henrik Grubbström (Grubba)  "Connection":"", "Expires":"",
3344142008-05-13Martin Stjernholm  "Cache-Control": "",
56f6952009-01-28Martin Jonsson #ifdef HTTP_COMPRESSION "Content-Encoding":"", "ETag":"", // Keeping the ETag // while transforming // content (gzip) may // confuse clients. #endif
94e9ac2005-12-05Henrik Grubbström (Grubba)  ]) & heads;
a5c6e92005-11-25Henrik Grubbström (Grubba)  m_delete(heads, "Date"); m_delete(heads, "Content-Type"); m_delete(heads, "Content-Length"); m_delete(heads, "Connection");
94e9ac2005-12-05Henrik Grubbström (Grubba)  m_delete(heads, "Expires");
3344142008-05-13Martin Stjernholm  m_delete(heads, "Cache-Control");
56f6952009-01-28Martin Jonsson #ifdef HTTP_COMPRESSION m_delete(heads, "Content-Encoding"); m_delete(heads, "ETag"); #endif
a5c6e92005-11-25Henrik Grubbström (Grubba)  // FIXME: prot.
1f57322008-05-07Martin Stjernholm  head_string = sprintf(" %s\r\n", head_status || Roxen.http_status_messages[file->error] || "");
2bfbd92004-04-13Martin Stjernholm 
a5c6e92005-11-25Henrik Grubbström (Grubba)  if (mixed err = catch(head_string += Roxen.make_http_headers(heads, 1))) {
06e9582009-09-22Martin Stjernholm  report_debug("Roxen.make_http_headers failed on %O: %s", heads, describe_error (err));
a5c6e92005-11-25Henrik Grubbström (Grubba)  foreach(heads; string x; string|array(string) val) { if( !arrayp( val ) ) val = ({val}); foreach( val, string xx ) { if (!stringp (xx) && catch {xx = (string) xx;}) report_error("Error in request for %O:\n" "Invalid value for header %O: %O\n", raw_url, x, xx); else if (String.width (xx) > 8) report_error("Error in request for %O:\n" "Invalid widestring value for header %O: %O\n", raw_url, x, xx); else head_string += x+": "+xx+"\r\n"; }
c1413f2005-10-28Henrik Grubbström (Grubba)  }
a5c6e92005-11-25Henrik Grubbström (Grubba)  }
c1413f2005-10-28Henrik Grubbström (Grubba) 
d7427c2006-04-20Henrik Grubbström (Grubba)  if (objectp(cookies)) { // Disconnect the cookie jar.
abe6ce2006-08-16Henrik Grubbström (Grubba)  real_cookies = cookies = ~cookies;
d7427c2006-04-20Henrik Grubbström (Grubba)  }
74da3d2008-01-10Martin Stjernholm  int varies = misc->vary && (sizeof(misc->vary) - misc->vary["host"]);
5f6dae2000-08-13Per Hedbor #ifdef RAM_CACHE
4511982006-09-21Henrik Grubbström (Grubba)  if( (misc->cacheable > 0) && !misc->no_proto_cache) { if ((<"HEAD","GET">)[method]) {
a5c6e92005-11-25Henrik Grubbström (Grubba)  if( file->len>0 && // known length.
c1413f2005-10-28Henrik Grubbström (Grubba)  ((file->len + sizeof(head_string)) <
0248d42004-05-25Martin Stjernholm  conf->datacache->max_file_size) // vvv Relying on the interpreter lock from here.
a5c6e92005-11-25Henrik Grubbström (Grubba)  && misc->cachekey )
0248d42004-05-25Martin Stjernholm  { misc->cachekey->activate(); // ^^^ Relying on the interpreter lock to here. string data = "";
a5c6e92005-11-25Henrik Grubbström (Grubba)  if( file->data ) data = file->data[..file->len-1]; if( file->file ) data = file->file->read(file->len);
56f6952009-01-28Martin Jonsson  #ifdef HTTP_COMPRESSION string uncompressed = data; string compressed;
ffc7092009-05-07Stephen R. van den Berg  string encoding = file->encoding; if(!encoding) {
cda0322009-04-17Stephen R. van den Berg  if(compressed = try_gzip_data(data, variant_heads["Content-Type"])) {
56f6952009-01-28Martin Jonsson  data = compressed;
ffc7092009-05-07Stephen R. van den Berg  encoding = "gzip";
56f6952009-01-28Martin Jonsson  } } #endif
526f5c2009-01-10Stephen R. van den Berg  conf->datacache->set(misc->prot_cache_key, data,
8b07f92005-11-24Henrik Grubbström (Grubba)  ([ "hs":head_string, "key":misc->cachekey, "etag":misc->etag, "callbacks":misc->_cachecallbacks, "len":file->len, "raw":file->raw, "error":file->error,
a5c6e92005-11-25Henrik Grubbström (Grubba)  "type":variant_heads["Content-Type"],
8b07f92005-11-24Henrik Grubbström (Grubba)  "last_modified":misc->last_modified,
94e9ac2005-12-05Henrik Grubbström (Grubba)  "varies":varies, "expires":variant_heads["Expires"],
3344142008-05-13Martin Stjernholm  "cache_control":variant_heads["Cache-Control"],
a5c6e92005-11-25Henrik Grubbström (Grubba)  "mtime":(file->stat && file->stat[ST_MTIME]),
8b07f92005-11-24Henrik Grubbström (Grubba)  "rf":realfile,
b418a42007-09-04Henrik Grubbström (Grubba)  "refresh":predef::time(1) + 5 + 3*misc->cacheable/4,
56f6952009-01-28Martin Jonsson #ifdef HTTP_COMPRESSION
ffc7092009-05-07Stephen R. van den Berg  "encoding" : encoding,
56f6952009-01-28Martin Jonsson #endif
8b07f92005-11-24Henrik Grubbström (Grubba)  ]),
1455922005-12-05Henrik Grubbström (Grubba)  misc->cacheable, this_object());
a5c6e92005-11-25Henrik Grubbström (Grubba)  file = ([
56f6952009-01-28Martin Jonsson #ifndef HTTP_COMPRESSION "data":data,
a5c6e92005-11-25Henrik Grubbström (Grubba)  "len":strlen(data),
56f6952009-01-28Martin Jonsson #endif "raw":file->raw,
a5c6e92005-11-25Henrik Grubbström (Grubba)  "error":file->error,
29fc822006-10-27Henrik Grubbström (Grubba)  "type":variant_heads["Content-Type"],
a5c6e92005-11-25Henrik Grubbström (Grubba)  ]);
56f6952009-01-28Martin Jonsson  #ifdef HTTP_COMPRESSION
cf81c82009-01-29Martin Jonsson  if(!misc->range && compressed && client_gzip_enabled()) {
56f6952009-01-28Martin Jonsson  file->data = compressed; file->encoding = encoding; file->compressed = 1; } else { file->data = uncompressed; } file->len = sizeof(file->data); variant_heads["Content-Length"] = (string)file->len; #endif
970cc92006-04-20Henrik Grubbström (Grubba)  cache_status["protstore"] = 1;
a5c6e92005-11-25Henrik Grubbström (Grubba)  }
5f6dae2000-08-13Per Hedbor  }
4511982006-09-21Henrik Grubbström (Grubba)  }
5f6dae2000-08-13Per Hedbor #endif
4511982006-09-21Henrik Grubbström (Grubba) 
56f6952009-01-28Martin Jonsson #ifdef HTTP_COMPRESSION
cf81c82009-01-29Martin Jonsson  // Don't use compression if we're dealing with byte ranges. Who // knows what kind of interesting effects that might have.
c993712010-09-02Martin Jonsson  if(!file->compressed && (file->data || file->file) && !misc->range &&
56f6952009-01-28Martin Jonsson  compress_dynamic_requests() && client_gzip_enabled()) {
c993712010-09-02Martin Jonsson  if (mixed /*Stdio.File|string*/ compressed = try_gzip_data(file->data ? file->data : file->file, file->type)) { if (file->data) { file->data = compressed; file->len = sizeof(file->data); variant_heads["Content-Length"] = (string)file->len; } else if (file->file) { file->file = compressed; m_delete (file, "len"); // We don't know the length since we will be streaming compressed data... m_delete (variant_heads, "Content-Length"); }
56f6952009-01-28Martin Jonsson  file->encoding = "gzip";
c993712010-09-02Martin Jonsson  file->compressed = 1;
56f6952009-01-28Martin Jonsson  } }
3754852009-01-29Martin Jonsson  if(file->compressed && misc->etag) { string etag = misc->etag; if(etag[sizeof(etag)-1..] == "\"") misc->etag = etag[..sizeof(etag)-2] + ";gzip\""; }
56f6952009-01-28Martin Jonsson #endif
4511982006-09-21Henrik Grubbström (Grubba)  // 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 ) { variant_heads->Connection = "close"; misc->connection = "close"; } 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; }
94e9ac2005-12-05Henrik Grubbström (Grubba)  }
4511982006-09-21Henrik Grubbström (Grubba)  if(since && misc->last_modified && (conditional >= 0)) { /* ({ time, len }) */ array(int) since_info = Roxen.parse_since( since ); // 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)) // actually ok, or... // || ((misc->cacheable>0) // && (since_info[0] + misc->cacheable<= predef::time(1)) // // cacheable, and not enough time has passed. ) { conditional = conditional || 304; } else { conditional = -1; } } if (conditional > 0) { // All conditionals apply. file->error = conditional; file->file = file->data = file->len = 0; // Must update the content length after the modifications of the // data to send that might have been done above for 206 or 304. variant_heads["Content-Length"] = "0"; } } if (varies && (prot == "HTTP/1.0")) { // The Vary header is new in HTTP/1.1. // It expired a year ago.
49681e2006-12-14Henrik Grubbström (Grubba) #ifndef DISABLE_VARY_EXPIRES_FALLBACK
4511982006-09-21Henrik Grubbström (Grubba)  variant_heads["Expires"] = Roxen->http_date(predef::time(1)-31557600);
49681e2006-12-14Henrik Grubbström (Grubba) #endif /* !DISABLE_VARY_EXPIRES_FALLBACK */
4511982006-09-21Henrik Grubbström (Grubba)  } if( (method == "HEAD") || (file->error == 204) || (file->error == 304) || (file->error < 200)) { // RFC 2068 4.4.1 // Any response message which MUST NOT include a message-body // (such as the 1xx, 204, and 304 responses and any response // to a HEAD request) is always terminated by the first empty // line after the header fields, regardless of the entity-header // fields present in the message. file->len = 1; // Keep those alive, please... file->data = ""; file->file = 0; } else {
56f6952009-01-28Martin Jonsson #ifdef HTTP_COMPRESSION if(misc->etag) variant_heads["ETag"] = misc->etag; if(file->encoding) variant_heads["Content-Encoding"] = file->encoding; // Perhaps set some gzip log status if file->encoding == "gzip" here? #endif
496c842005-11-25Henrik Grubbström (Grubba)  if (misc->range) { // Handle byte ranges.
c6f7be2005-11-28Henrik Grubbström (Grubba)  int skip; string if_range; if (if_range = request_headers["if-range"]) { // Check If-Range header (RFC 2068 14.27). if (has_prefix(if_range, "\"")) { // ETag if (if_range != misc->etag) { // ETag has changed. skip = 1; } } else { array(int) since_info = Roxen.parse_since(if_range); if (!since_info || (since_info[0] < misc->last_modified)) { // Failed to parse since info, or the file has changed. skip = 1; } } } if (!skip) { // NOTE: Modifies both arguments destructively. handle_byte_ranges(file, variant_heads); }
a5c6e92005-11-25Henrik Grubbström (Grubba)  }
5f6dae2000-08-13Per Hedbor  }
0248d42004-05-25Martin Stjernholm 
a5c6e92005-11-25Henrik Grubbström (Grubba)  variant_string = Roxen.make_http_headers(variant_heads); full_headers = prot + " " + file->error + head_string + variant_string; low_send_result(full_headers, file->data, file->len, file->file); } else { // RAW or HTTP/0.9 mode. if(!file->type) file->type="text/plain";
496c842005-11-25Henrik Grubbström (Grubba)  // No headers!
a5c6e92005-11-25Henrik Grubbström (Grubba)  low_send_result("", file->data, file->len, file->file); } MARK_FD("HTTP handled");
9c19002001-02-27Per Hedbor  TIMER_END(send_result);
14179b1997-01-29Per Hedbor }
cea62a2010-08-19Martin Stjernholm // Execute the request. This is called from a handler thread.
d943c81998-05-18Henrik Grubbström (Grubba) void handle_request( ) {
6a409d1999-12-27Martin Nilsson  REQUEST_WERR("HTTP: handle_request()");
9c19002001-02-27Per Hedbor  TIMER_START(handle_request);
24a43a2009-06-10Martin Stjernholm  queue_time = gethrtime() - queue_time;
d943c81998-05-18Henrik Grubbström (Grubba) #ifdef MAGIC_ERROR if(prestate->old_error) {
d8947f2002-07-03Henrik Grubbström (Grubba)  array err = get_error(variables->error, variables->error_md5 || "NONE");
84732f2002-07-02Anders Johansson  if(err && arrayp(err))
d943c81998-05-18Henrik Grubbström (Grubba)  { 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)  ]);
9c19002001-02-27Per Hedbor  TIMER_END(handle_request);
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 ), ]);
9c19002001-02-27Per Hedbor  TIMER_END(handle_request);
8a80d41999-05-20Per Hedbor  send_result(); return;
d943c81998-05-18Henrik Grubbström (Grubba)  } } } } #endif /* MAGIC_ERROR */ MARK_FD("HTTP handling request");
24a43a2009-06-10Martin Stjernholm  handle_time = gethrtime(); #if constant(System.CPU_TIME_IS_THREAD_LOCAL) handle_vtime = gethrvtime(); #endif
c51f2b2003-11-03Martin Stjernholm  mapping result;
24a43a2009-06-10Martin Stjernholm  array e = catch(result = conf->handle_request( this_object() ));
3045db2009-09-21Martin Stjernholm  // Note: Could be destructed here already since handle_request might // have handed over us to another thread that finished quickly. The // right place to put code is probably send_result instead.
cea62a2010-08-19Martin Stjernholm  // // I wonder exactly where that handing-over might happen. There // should probably be destruct_threadbound_session_objects calls // there too. /mast if (this) destruct_threadbound_session_objects();
24a43a2009-06-10Martin Stjernholm  if(e)
5496fd2003-09-17Henrik Grubbström (Grubba)  INTERNAL_ERROR( e );
c51f2b2003-11-03Martin Stjernholm 
f0e7bc2004-01-19Martin Stjernholm  else {
9e67a42007-06-12Henrik Grubbström (Grubba)  if (result && result->pipe) { REQUEST_WERR("HTTP: handle_request: pipe in progress."); TIMER_END(handle_request);
f0e7bc2004-01-19Martin Stjernholm  return;
9e67a42007-06-12Henrik Grubbström (Grubba)  }
f0e7bc2004-01-19Martin Stjernholm  file = result; }
c51f2b2003-11-03Martin Stjernholm  if( file && file->try_again_later ) { if( objectp( file->try_again_later ) ) ; else call_out( roxen.handle, file->try_again_later, handle_request );
9e67a42007-06-12Henrik Grubbström (Grubba)  TIMER_END(handle_request);
c51f2b2003-11-03Martin Stjernholm  return;
d7e1b42003-06-16Henrik Grubbström (Grubba)  }
c51f2b2003-11-03Martin Stjernholm 
3088b12003-07-16Henrik Grubbström (Grubba)  TIMER_END(handle_request); 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  */
0d0e952000-11-13Per Hedbor // array ccd = ({});
4a98402004-08-18Martin Stjernholm void got_data(mixed fooid, string s, void|int chained)
14179b1997-01-29Per Hedbor {
acd7242004-04-27Martin Stjernholm #ifdef CONNECTION_DEBUG
1139a12006-10-13Martin Stjernholm  werror ("HTTP[%s]: Request ----------------------------------------------\n"
7a14242009-01-07Martin Stjernholm  "%O\n", DEBUG_GET_FD, s);
acd7242004-04-27Martin Stjernholm #else
f136c22002-02-26Martin Stjernholm  REQUEST_WERR(sprintf("HTTP: Got %O", s));
acd7242004-04-27Martin Stjernholm #endif
d0739e2002-02-14Henrik Grubbström (Grubba) 
e885f72010-06-29Henrik Grubbström (Grubba)  // Keep track of how much data we've received. raw_bytes += sizeof(s);
0d0e952000-11-13Per Hedbor  if(wanted_data)
d042a82000-08-31Per Hedbor  {
ae2d0e2002-11-18Henrik Grubbström (Grubba)  // NOTE: No need to make a data buffer if it's a small request.
0d0e952000-11-13Per Hedbor  if(strlen(s) + have_data < wanted_data)
d042a82000-08-31Per Hedbor  {
ae2d0e2002-11-18Henrik Grubbström (Grubba)  if (!data_buffer) {
f133c62002-11-18Henrik Grubbström (Grubba)  // The 16384 is some reasonable extra padding to // avoid having to realloc. data_buffer = String.Buffer(wanted_data + 16384);
ae2d0e2002-11-18Henrik Grubbström (Grubba)  data_buffer->add(data); data = ""; } data_buffer->add(s);
0d0e952000-11-13Per Hedbor  have_data += strlen(s);
ae2d0e2002-11-18Henrik Grubbström (Grubba) 
4a98402004-08-18Martin Stjernholm  REQUEST_WERR("HTTP: We want more data.");
e8c7102002-07-10Anders Johansson  // Reset timeout. remove_call_out(do_timeout); call_out(do_timeout, 90);
4a98402004-08-18Martin Stjernholm  if (chained) my_fd->set_nonblocking(got_data, 0, close_cb);
0d0e952000-11-13Per Hedbor  return; }
ae2d0e2002-11-18Henrik Grubbström (Grubba)  if (data_buffer) { data_buffer->add(s);
1752c32003-04-22Dan Nelson  data = data_buffer->get();
ae2d0e2002-11-18Henrik Grubbström (Grubba)  data_buffer = 0; } else { data += s; }
3e3f202009-06-03Henrik Grubbström (Grubba)  } else if (misc->chunked) { if (!got_chunk_fragment(s)) { REQUEST_WERR("HTTP: We want more data (chunked)."); // Reset timeout. remove_call_out(do_timeout); call_out(do_timeout, 90); if (chained) my_fd->set_nonblocking(got_data, 0, close_cb); return; }
e885f72010-06-29Henrik Grubbström (Grubba)  s = "";
d042a82000-08-31Per Hedbor  }
acd34a2000-01-22Martin Stjernholm  if (mixed err = catch {
26b0b92001-10-02Per Hedbor  MARK_FD("HTTP got data"); raw += s;
0d0e952000-11-13Per Hedbor 
26b0b92001-10-02Per Hedbor  // The port has been closed, but old (probably keep-alive) // connections remain. Close those connections. if( !port_obj )
14179b1997-01-29Per Hedbor  {
b611c62002-03-27Per Hedbor  if( conf ) conf->connection_drop( this_object() );
93aa102003-11-17Martin Stjernholm  MARK_FD ("HTTP: Port closed."); call_out (disconnect, 0);
26b0b92001-10-02Per Hedbor  return; }
abdff21999-12-27Martin Nilsson 
26b0b92001-10-02Per Hedbor  switch( parse_got( s ) ) { case 0: REQUEST_WERR("HTTP: Request needs more data.");
4a98402004-08-18Martin Stjernholm  if (chained) my_fd->set_nonblocking(got_data, 0, close_cb);
26b0b92001-10-02Per Hedbor  return;
26f44e2000-08-13Per Hedbor 
96cf672009-01-15Martin Stjernholm  case 1: { string res = (prot||"HTTP/1.0")+" 500 Illegal request\r\n" "Content-Length: 0\r\n"+ "Date: "+Roxen.http_date(predef::time())+"\r\n" "\r\n"; #ifdef CONNECTION_DEBUG werror ("HTTP[%s]: Response (length %d) =================================\n" "%O\n", DEBUG_GET_FD, sizeof (res), res); #else
f136c22002-02-26Martin Stjernholm  REQUEST_WERR("HTTP: Stupid Client Error.");
96cf672009-01-15Martin Stjernholm #endif my_fd->write (res);
26b0b92001-10-02Per Hedbor  end(); return; // Stupid request.
96cf672009-01-15Martin Stjernholm  }
26b0b92001-10-02Per Hedbor  case 2:
f136c22002-02-26Martin Stjernholm  REQUEST_WERR("HTTP: Done.");
26b0b92001-10-02Per Hedbor  end(); return; }
abdff21999-12-27Martin Nilsson 
acd7242004-04-27Martin Stjernholm #ifdef CONNECTION_DEBUG
1139a12006-10-13Martin Stjernholm  werror ("HTTP[%s]: Request received -------------------------------------\n", DEBUG_GET_FD);
acd7242004-04-27Martin Stjernholm #endif
be97162003-11-25Anders Johansson  if( method == "GET" || method == "HEAD" ) {
3d2ce82005-11-24Henrik Grubbström (Grubba)  // NOTE: Setting misc->cacheable enables use of the RAM_CACHE.
26b0b92001-10-02Per Hedbor  misc->cacheable = INITIAL_CACHEABLE; // FIXME: Make configurable.
be97162003-11-25Anders Johansson #ifdef DEBUG_CACHEABLE report_debug("===> Request for %s initiated cacheable to %d.\n", raw_url, misc->cacheable); #endif }
abdff21999-12-27Martin Nilsson 
26b0b92001-10-02Per Hedbor  TIMER_START(find_conf);
8b07f92005-11-24Henrik Grubbström (Grubba) 
26b0b92001-10-02Per Hedbor  string path;
a2a5aa2000-08-31Per Hedbor 
8b07f92005-11-24Henrik Grubbström (Grubba)  // RFC 2068 5.1.2: // // To allow for transition to absoluteURIs in all requests in future // versions of HTTP, all HTTP/1.1 servers MUST accept the absoluteURI // form in requests, even though HTTP/1.1 clients will only generate // them in requests to proxies.
526f5c2009-01-10Stephen R. van den Berg  misc->prot_cache_key = raw_url;
8b07f92005-11-24Henrik Grubbström (Grubba)  if (has_prefix(raw_url, port_obj->url_prefix)) { sscanf(raw_url[sizeof(port_obj->url_prefix)..], "%[^/]%s", misc->host, raw_url); }
1985942009-01-21Martin Stjernholm  string canon_hostport; if (string host = misc->host) { // Parse and canonicalize the host header for use in the url // used for port matching. int port = port_obj->default_port; if (has_prefix(host, "[")) {
dc89282008-12-11Jonas Wallden  // IPv6 address
1985942009-01-21Martin Stjernholm  sscanf(lower_case(host), "[%s]:%d", host, port); host = Protocols.IPv6.normalize_addr_basic (host) || host; canon_hostport = "[" + host + "]:" + port;
dc89282008-12-11Jonas Wallden  } else {
1985942009-01-21Martin Stjernholm  sscanf(lower_case(host), "%[^:]:%d", host, port); canon_hostport = host + ":" + port;
dc89282008-12-11Jonas Wallden  }
1985942009-01-21Martin Stjernholm  misc->hostname = host; misc->port = port;
8b07f92005-11-24Henrik Grubbström (Grubba)  }
1985942009-01-21Martin Stjernholm 
8b07f92005-11-24Henrik Grubbström (Grubba)  if( !conf || !(path = port_obj->path ) || (sizeof( path ) && !has_prefix(raw_url, path)) ) {
26b0b92001-10-02Per Hedbor  // FIXME: port_obj->name & port_obj->default_port are constant // consider caching them?
0b03222001-11-14Henrik Grubbström (Grubba) 
1985942009-01-21Martin Stjernholm  string port_match_url = (port_obj->url_prefix + (canon_hostport || ("*:" + port_obj->port)) + raw_url);
526f5c2009-01-10Stephen R. van den Berg  conf = port_obj->find_configuration_for_url(port_match_url, this); // Note: The call above might have replaced port_obj from one // bound to a specific interface to one bound to ANY. if (misc->defaulted_conf > 1) // Use the full url in the cache if a fallback configuration // (with or without the default_server flag) was chosen. misc->prot_cache_key = port_match_url;
26b0b92001-10-02Per Hedbor  } else if( strlen(path) ) adjust_for_config_path( path );
0aee222000-08-15Martin Stjernholm 
26b0b92001-10-02Per Hedbor  TIMER_END(find_conf);
26f44e2000-08-13Per Hedbor 
8e89b02008-10-10Martin Stjernholm  // The "http_request_init" provider hook allows modules to do // things very early in the request path, before the request is // put in the handler queue and before the protocol cache is // queried. // // Use with great care; this is run in the backend thread, and // some things in the id object are still not initialized. foreach (conf->get_providers ("http_request_init"), RoxenModule mod) if (mapping res = mod->http_request_init (this)) {
e885f72010-06-29Henrik Grubbström (Grubba)  conf->received += raw_bytes - sizeof(leftovers);
8e89b02008-10-10Martin Stjernholm  conf->requests++; // RequestID.make_response_headers assumes the supports multiset exists. if (!supports) supports = (<>); send_result (res); return; }
26b0b92001-10-02Per Hedbor  if (rawauth)
c5e0961999-10-04Per Hedbor  {
26b0b92001-10-02Per Hedbor  /* Need to authenticate with the configuration */
d7427c2006-04-20Henrik Grubbström (Grubba)  NO_PROTO_CACHE();
26b0b92001-10-02Per Hedbor  array(string) y = rawauth / " "; realauth = 0; auth = 0; if (sizeof(y) >= 2) { y[1] = MIME.decode_base64(y[1]); realauth = y[1]; }
c5e0961999-10-04Per Hedbor  }
26b0b92001-10-02Per Hedbor  if( misc->proxyauth )
c5e0961999-10-04Per Hedbor  {
26b0b92001-10-02Per Hedbor  /* Need to authenticate with the configuration */
d7427c2006-04-20Henrik Grubbström (Grubba)  NO_PROTO_CACHE();
26b0b92001-10-02Per Hedbor  if (sizeof(misc->proxyauth) >= 2) { // misc->proxyauth[1] = MIME.decode_base64(misc->proxyauth[1]);
2a74052009-04-28Henrik Grubbström (Grubba)  // // FIXME: Obsolete API!
26b0b92001-10-02Per Hedbor  if (conf->auth_module) misc->proxyauth = conf->auth_module->auth(misc->proxyauth,this_object() ); }
c5e0961999-10-04Per Hedbor  }
2154ca1997-08-31Per Hedbor 
b611c62002-03-27Per Hedbor  conf->connection_add( this_object(), connection_stats );
e885f72010-06-29Henrik Grubbström (Grubba)  conf->received += raw_bytes - sizeof(leftovers);
26b0b92001-10-02Per Hedbor  conf->requests++;
39202e2003-11-03Martin Stjernholm  CHECK_FD_SAFE_USE;
26b0b92001-10-02Per Hedbor  my_fd->set_close_callback(0); my_fd->set_read_callback(0);
5f6dae2000-08-13Per Hedbor 
26b0b92001-10-02Per Hedbor  remove_call_out(do_timeout);
5f6dae2000-08-13Per Hedbor #ifdef RAM_CACHE
26b0b92001-10-02Per Hedbor  TIMER_START(cache_lookup); array cv;
d7427c2006-04-20Henrik Grubbström (Grubba)  if(misc->cacheable && !misc->no_proto_cache &&
526f5c2009-01-10Stephen R. van den Berg  (cv = conf->datacache->get(misc->prot_cache_key, this)) )
99b98b2000-08-14Per Hedbor  {
526f5c2009-01-10Stephen R. van den Berg  MY_TRACE_ENTER(sprintf("Checking entry %O", misc->prot_cache_key));
26b0b92001-10-02Per Hedbor  if( !cv[1]->key ) {
3d2ce82005-11-24Henrik Grubbström (Grubba)  MY_TRACE_LEAVE("Entry invalid due to zero key");
526f5c2009-01-10Stephen R. van den Berg  conf->datacache->expire_entry(misc->prot_cache_key, this);
c3c6322000-09-20Per Hedbor  }
26b0b92001-10-02Per Hedbor  else
0a900b2000-08-17Per Hedbor  {
26b0b92001-10-02Per Hedbor  int can_cache = 1; string d = cv[ 0 ];
c7429a2009-04-17Martin Stjernholm #ifdef DEBUG if (!stringp (d)) error ("Strange value from data cache for %O: %O\n", raw_url, cv); #endif
26b0b92001-10-02Per Hedbor  file = cv[1]; if( sizeof(file->callbacks) ) { if( mixed e = catch { foreach( file->callbacks, function f ) {
814fbc2007-09-21Henrik Grubbström (Grubba)  if (!file->key) break;
ae79002008-11-05Martin Stjernholm  MY_TRACE_ENTER (sprintf ("Checking with %O", f));
b017232007-06-12Henrik Grubbström (Grubba)  if( !f(this_object(), file->key ) )
26b0b92001-10-02Per Hedbor  { MY_TRACE_LEAVE ("Entry invalid according to callback"); MY_TRACE_LEAVE (""); can_cache = 0; break; } MY_TRACE_LEAVE (""); } } ) {
3e333f2006-01-02Henrik Grubbström (Grubba)  // Callback failed; in destructed object? if (e = catch { werror("Cache callback internal server error:\n" "%s\n", describe_backtrace(e)); // Invalidate the key.
b017232007-06-12Henrik Grubbström (Grubba)  destruct(file->key);
3e333f2006-01-02Henrik Grubbström (Grubba)  }) { // Fall back to a standard internal error. INTERNAL_ERROR( e ); TIMER_END(cache_lookup); send_result(); return; }
26b0b92001-10-02Per Hedbor  } }
814fbc2007-09-21Henrik Grubbström (Grubba)  if(roxen.invalidp(file->key))
26b0b92001-10-02Per Hedbor  {
814fbc2007-09-21Henrik Grubbström (Grubba)  // Stale or invalid key. if (!file->key) { // Invalid. MY_TRACE_LEAVE ("Entry invalid due to zero key");
526f5c2009-01-10Stephen R. van den Berg  conf->datacache->expire_entry(misc->prot_cache_key, this);
814fbc2007-09-21Henrik Grubbström (Grubba)  can_cache = 0;
a82cb52008-04-28Martin Stjernholm  } else { cache_status["stale"] = 1; if (file->refresh > predef::time(1)) { // Stale and no refresh in progress. // We want a refresh as soon as possible, // so move the refresh time to now. // Note that we use the return value from m_delete() // to make sure we are free from races. // Note also that we check above that the change is needed, // so as to avoid the risk of starving the code below. if (m_delete(file, "refresh")) { file->refresh = predef::time(1); }
814fbc2007-09-21Henrik Grubbström (Grubba)  } } }
26b0b92001-10-02Per Hedbor  if( can_cache ) {
a2a5aa2000-08-31Per Hedbor #ifndef RAM_CACHE_ASUME_STATIC_CONTENT
26b0b92001-10-02Per Hedbor  Stat st; if( !file->rf || !file->mtime || ((st = file_stat( file->rf )) && st->mtime == file->mtime ))
a2a5aa2000-08-31Per Hedbor #endif
26b0b92001-10-02Per Hedbor  {
814fbc2007-09-21Henrik Grubbström (Grubba)  int refresh; if (file->refresh && (file->refresh <= predef::time(1))) {
7329de2007-09-12Henrik Grubbström (Grubba)  // We might need to refresh the entry. // Note that we use the return value from m_delete() // to make sure we are free from races.
814fbc2007-09-21Henrik Grubbström (Grubba)  if (refresh = m_delete(file, "refresh")) {
7329de2007-09-12Henrik Grubbström (Grubba)  refresh = 1 + predef::time(1) - refresh; }
b017232007-06-12Henrik Grubbström (Grubba)  }
27ef7f2005-11-18Henrik Grubbström (Grubba)  int code = file->error; int len = sizeof(d);
56f6952009-01-28Martin Jonsson  mapping(string:string) variant_heads = ([]); #ifdef HTTP_COMPRESSION
9cc3552009-05-05Martin Jonsson  if(file->etag) variant_heads["ETag"] = file->etag;
56f6952009-01-28Martin Jonsson  if(file->encoding == "gzip") {
cf81c82009-01-29Martin Jonsson  if(!misc->range && client_gzip_enabled()) {
56f6952009-01-28Martin Jonsson  variant_heads["Content-Encoding"] = file->encoding; if(file->etag) { string etag = file->etag;
655ae92009-03-21Martin Stjernholm  if(etag[sizeof(etag)-1..] == "\"")
56f6952009-01-28Martin Jonsson  etag = etag[..sizeof(etag)-2] + ";gzip\""; variant_heads["ETag"] = etag; } // Perhaps set some gzip log status here? } else { d = gunzip_data(d); len = sizeof(d); } } #endif
c24a482005-12-13Anders Johansson  // Make sure we don't mess with the RAM cache. file += ([]);
65e9af2008-02-28Jonas Wallden  if (none_match) { // RFC 2616, Section 14.26: // // If none of the entity tags match, then the server // MAY perform the requested method as if the // If-None-Match header field did not exist, but MUST // also ignore any If-Modified-Since header field(s) in // the request. That is, if no entity tags match, then // the server MUST NOT return a 304 (Not Modified) // response. if (none_match[file->etag] || (none_match["*"] && file->etag)) { // Not modified. code = 304; d = ""; len = 0; }
e4ae732007-08-28Arjan van Staalduijnen  } else if (since && file->last_modified) {
27ef7f2005-11-18Henrik Grubbström (Grubba)  array(int) since_info = Roxen.parse_since( since ); if ((since_info[0] >= file->last_modified) && ((since_info[1] == -1) || (since_info[1] == len))) { // Not modified. code = 304; d = ""; len = 0; } }
c24a482005-12-13Anders Johansson  file->error = code;
27ef7f2005-11-18Henrik Grubbström (Grubba)  if (method == "HEAD") { d = ""; }
56f6952009-01-28Martin Jonsson  variant_heads += ([
496c842005-11-25Henrik Grubbström (Grubba)  "Date":Roxen.http_date(predef::time(1)), "Content-Length":(string)len, "Content-Type":file->type,
3ce2fa2005-12-05Henrik Grubbström (Grubba)  "Connection":misc->connection,
496c842005-11-25Henrik Grubbström (Grubba)  ]);
3344142008-05-13Martin Stjernholm  // Don't allow any downstream caching if the response is stale. One // reason for this is that if a client has been notified somehow // about the change that has made this entry stale, and then // proceeds to retrieve the resource to get the new version, it at // least shouldn't overcache the response if it's unlucky and gets // the old stale version. (One might consider disabling this if all // clients can be assumed to be end users.) if (cache_status->stale) { variant_heads["Cache-Control"] = "no-cache"; // RFC2616, 14.9.3: "If a cache returns a stale response, /.../, // the cache MUST attach a Warning header to the stale response, // using Warning 110 (Response is stale)." variant_heads["Warning"] = "110 " + replace (my_fd->query_address (1) || "roxen", " ", ":") + " \"Response is stale - update is underway\" " "\"" + variant_heads["Date"] + "\""; } else if (string cc = file->cache_control) variant_heads["Cache-Control"] = cc;
c691fe2005-12-07Henrik Grubbström (Grubba)  string expires;
3344142008-05-13Martin Stjernholm  if (expires = (cache_status->stale // See above.
8021152008-05-12Henrik Grubbström (Grubba) #ifndef DISABLE_VARY_EXPIRES_FALLBACK
3344142008-05-13Martin Stjernholm  || (file->varies && (prot == "HTTP/1.0"))
8021152008-05-12Henrik Grubbström (Grubba) #endif /* !DISABLE_VARY_EXPIRES_FALLBACK */
3344142008-05-13Martin Stjernholm  ? Roxen->http_date(predef::time(1)-31557600) :
c691fe2005-12-07Henrik Grubbström (Grubba)  file->expires)) { variant_heads["Expires"] = expires; }
4511982006-09-21Henrik Grubbström (Grubba)  // 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 ) { variant_heads->Connection = "close"; misc->connection = "close"; }
496c842005-11-25Henrik Grubbström (Grubba)  if (misc->range) { // Handle byte ranges.
c6f7be2005-11-28Henrik Grubbström (Grubba)  int skip; string if_range; if (if_range = request_headers["if-range"]) { // Check If-Range header (RFC 2068 14.27). if (has_prefix(if_range, "\"")) { // ETag if (if_range != file->etag) { // ETag has changed. skip = 1; } } else { array(int) since_info = Roxen.parse_since(if_range); if (!since_info || (since_info[0] < file->last_modified)) { // Failed to parse since info, or the file has changed. skip = 1; } } } if (!skip) { file->data = d; file->len = len; // NOTE: Modifies both arguments destructively. handle_byte_ranges(file, variant_heads); d = file->data; code = file->error; }
496c842005-11-25Henrik Grubbström (Grubba)  }
d7427c2006-04-20Henrik Grubbström (Grubba)  string full_headers = ""; if (prot != "HTTP/0.9") { full_headers = prot + " " + code + file->hs + Roxen.make_http_headers(variant_heads); }
c1413f2005-10-28Henrik Grubbström (Grubba) 
4337712008-01-09Martin Stjernholm  MY_TRACE_LEAVE ("Using entry from protocol cache");
26b0b92001-10-02Per Hedbor  cache_status["protcache"] = 1;
3d2ce82005-11-24Henrik Grubbström (Grubba) 
f2eaa32007-09-03Henrik Grubbström (Grubba)  if (!refresh) {
7329de2007-09-12Henrik Grubbström (Grubba)  // No need to refresh the cached entry, so we just send it,
0c272f2009-03-17Martin Jonsson  // disconnect the cookie jar, and are done. if (objectp(cookies)) { // Disconnect the cookie jar just before sending the reply. real_cookies = cookies = ~cookies; }
354cdd2007-09-04Henrik Grubbström (Grubba)  TIMER_END(cache_lookup);
f2eaa32007-09-03Henrik Grubbström (Grubba)  low_send_result(full_headers, d, sizeof(d)); return; } // Create a new RequestID for sending the cached response, // so that it won't interfere with this one when it finishes. // We need to copy lots of stuff to keep the log functions happy. RequestID id = clone_me(); id->hrtime = hrtime; id->my_fd = my_fd; id->file = file; id->kept_alive = kept_alive; id->cache_status = cache_status + (<>); id->eval_status = eval_status + (<>); id->output_charset = output_charset; id->input_charset = input_charset; id->auth = auth; id->throttle = throttle + ([]); id->throttler = throttler; conf->connection_add( id, connection_stats );
354cdd2007-09-04Henrik Grubbström (Grubba)  TIMER_END(cache_lookup);
f2eaa32007-09-03Henrik Grubbström (Grubba)  id->low_send_result(full_headers, d, sizeof(d));
61cb192008-01-08Martin Stjernholm 
8021152008-05-12Henrik Grubbström (Grubba)  method = "GET";
61cb192008-01-08Martin Stjernholm  remoteaddr = "127.0.0.1"; host = 0;
f2eaa32007-09-03Henrik Grubbström (Grubba)  my_fd = 0;
7329de2007-09-12Henrik Grubbström (Grubba)  misc->connection = "close";
f2eaa32007-09-03Henrik Grubbström (Grubba) 
b017232007-06-12Henrik Grubbström (Grubba)  MY_TRACE_ENTER (
3344142008-05-13Martin Stjernholm  sprintf("Starting refresh of stale entry "
ae79002008-11-05Martin Stjernholm  "(%d seconds past refresh time)", refresh - 1));
b017232007-06-12Henrik Grubbström (Grubba)  cache_status["protcache"] = 0; cache_status["refresh"] = 1;
a82cb52008-04-28Martin Stjernholm  cache_status["stale"] = 0;
b017232007-06-12Henrik Grubbström (Grubba)  MY_TRACE_LEAVE("");
26b0b92001-10-02Per Hedbor  }
773ac62001-03-20Martin Stjernholm #ifndef RAM_CACHE_ASUME_STATIC_CONTENT
26b0b92001-10-02Per Hedbor  else MY_TRACE_LEAVE ( sprintf ("Entry out of date (disk: %s, cache: mtime %d)", st ? "mtime " + st->mtime : "gone", file->mtime));
773ac62001-03-20Martin Stjernholm #endif
d7427c2006-04-20Henrik Grubbström (Grubba)  }
26b0b92001-10-02Per Hedbor  file = 0; }
99b98b2000-08-14Per Hedbor  }
26b0b92001-10-02Per Hedbor  TIMER_END(cache_lookup);
46d4cb2001-08-22Martin Stjernholm #endif // RAM_CACHE
26b0b92001-10-02Per Hedbor  TIMER_START(parse_request);
dcc8952001-10-09Marcus Wellhardh  if( things_to_do_when_not_sending_from_cache( ) ) return;
9caa2d2003-03-31Martin Stjernholm  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));
26b0b92001-10-02Per Hedbor  TIMER_END(parse_request);
a86c3b2000-08-28Per Hedbor 
f136c22002-02-26Martin Stjernholm  REQUEST_WERR("HTTP: Calling roxen.handle().");
24a43a2009-06-10Martin Stjernholm  queue_time = gethrtime();
e824002009-06-24Martin Stjernholm  queue_length = roxen.handle_queue_length();
26b0b92001-10-02Per Hedbor  roxen.handle(handle_request);
653d7d2000-02-14Per Hedbor  }) {
acd34a2000-01-22Martin Stjernholm  report_error("Internal server error: " + describe_backtrace(err)); 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;
a3ebea2000-08-12Per Hedbor  c=object_program(t=this_object())(0, port_obj, conf);
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 
0e5b1a1999-10-10Per Hedbor  c->port_obj = port_obj;
b1fca01996-11-12Per Hedbor  c->conf = conf;
ae727d2001-07-16Martin Nilsson  c->root_id = root_id;
b1fca01996-11-12Per Hedbor  c->time = time;
f6d62d1997-03-26Per Hedbor  c->raw_url = raw_url;
99cb5e2001-02-05Per Hedbor  c->real_variables = copy_value( real_variables ); c->variables = FakedVariables( c->real_variables );
a2a5aa2000-08-31Per Hedbor  c->misc = copy_value( misc );
48fa361997-04-05Per Hedbor  c->misc->orig = t;
f6d62d1997-03-26Per Hedbor 
99e65a2003-06-18Tomas Nilsson  c->connection_misc = connection_misc;
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;
11be9a2009-04-28Martin Jonsson  if (objectp(cookies)) { c->cookies = c->CookieJar(real_cookies + ([]));
2a74052009-04-28Henrik Grubbström (Grubba)  } else if (cookies) { c->cookies = c->real_cookies = real_cookies + ([]);
11be9a2009-04-28Martin Jonsson  } else {
e70c752009-04-28Henrik Grubbström (Grubba)  c->cookies = c->real_cookies = 0;
11be9a2009-04-28Martin Jonsson  }
e474072004-05-13Henrik Grubbström (Grubba)  c->request_headers = request_headers + ([]);
f6d62d1997-03-26Per Hedbor  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;
e885f72010-06-29Henrik Grubbström (Grubba)  c->raw_bytes = raw_bytes;
f6d62d1997-03-26Per Hedbor  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();
e2df6a2001-01-03Per Hedbor  else if((predef::time(1) - time) > 4800)
3235691998-03-26Per Hedbor  end();
b1fca01996-11-12Per Hedbor }
fc40392008-08-15Martin Stjernholm protected void create(object f, object c, object cc)
b1fca01996-11-12Per Hedbor {
14179b1997-01-29Per Hedbor  if(f) {
db38c12005-03-01Henrik Grubbström (Grubba) #if 0
5678e12003-11-04Martin Stjernholm  if (f->query_accept_callback)
1050aa2004-02-20Martin Stjernholm  f->set_nonblocking(got_data, f->query_write_callback(), close_cb, 0, 0,
5678e12003-11-04Martin Stjernholm  f->query_accept_callback()); else
db38c12005-03-01Henrik Grubbström (Grubba) #endif /* 0 */
1050aa2004-02-20Martin Stjernholm  f->set_nonblocking(got_data, f->query_write_callback(), close_cb);
14179b1997-01-29Per Hedbor  my_fd = f;
f0e7bc2004-01-19Martin Stjernholm  CHECK_FD_SAFE_USE;
f136c22002-02-26Martin Stjernholm  MARK_FD("HTTP connection");
a3ebea2000-08-12Per Hedbor  if( c ) port_obj = c; if( cc ) conf = cc;
e2df6a2001-01-03Per Hedbor  time = predef::time(1);
e409342006-09-21Marcus Wellhardh  hrtime = gethrtime();
491abc2000-08-17Per Hedbor  call_out(do_timeout, 90);
14179b1997-01-29Per Hedbor  }
ae727d2001-07-16Martin Nilsson  root_id = this_object();
b1fca01996-11-12Per Hedbor }
ceb9271997-05-15David Hedbor 
ebb1c51998-02-24Per Hedbor void chain(object f, object c, string le) { my_fd = f;
b303402004-08-11Henrik Grubbström (Grubba) 
5cf99d2005-11-18Henrik Grubbström (Grubba) #if defined(DEBUG) && defined(THREADS)
4a98402004-08-18Martin Stjernholm  if (this_thread() != roxen->backend_thread) error ("Not called from backend\n"); #endif
b303402004-08-11Henrik Grubbström (Grubba)  CHECK_FD_SAFE_USE;
870a9e1999-10-04Per Hedbor  port_obj = c;
f136c22002-02-26Martin Stjernholm  MARK_FD("HTTP kept alive");
a9feea2003-11-03Martin Stjernholm  time = predef::time();
8e5fa42006-09-21Marcus Wellhardh  hrtime = 0;
5f6dae2000-08-13Per Hedbor 
b692e02004-08-11Henrik Grubbström (Grubba)  if ( le && strlen( le ) ) { REQUEST_WERR(sprintf("HTTP: %d bytes left over.\n", sizeof(le)));
4a98402004-08-18Martin Stjernholm  got_data(0, le, 1);
b692e02004-08-11Henrik Grubbström (Grubba)  }
b303402004-08-11Henrik Grubbström (Grubba)  else { // If no pipelined data is available, call out... remove_call_out(do_timeout); call_out(do_timeout, 90);
4a98402004-08-18Martin Stjernholm  my_fd->set_nonblocking(got_data, 0, close_cb);
b303402004-08-11Henrik Grubbström (Grubba)  }
ebb1c51998-02-24Per Hedbor }
08bd3c1999-11-02Per Hedbor 
84fb682000-02-03Per Hedbor Stdio.File connection( ) { return my_fd; } Configuration configuration() { return conf; }