0247a31998-03-11David Hedbor // This is a roxen module. Copyright © 1996 - 1998, Idonex AB.
2a2a5b1996-12-01Per Hedbor 
f788481998-03-18Henrik Grubbström (Grubba) constant cvs_version = "$Id: http.pike,v 1.63 1998/03/18 16:56:34 grubba Exp $";
2a2a5b1996-12-01Per Hedbor // HTTP protocol module.
b1fca01996-11-12Per Hedbor #include <config.h>
fc2fcf1997-04-13Per Hedbor private inherit "roxenlib";
ebb1c51998-02-24Per Hedbor // int first;
8afc811998-02-04Per Hedbor #if efun(gethrtime) # define HRTIME() gethrtime() # define HRSEC(X) ((int)((X)*1000000)) # define SECHR(X) ((X)/(float)1000000) #else
c79b261998-02-05Johan Schön # define HRTIME() (predef::time())
8afc811998-02-04Per Hedbor # define HRSEC(X) (X) # define SECHR(X) ((float)(X)) #endif
ebb1c51998-02-24Per Hedbor #ifdef PROFILE
8afc811998-02-04Per Hedbor int req_time = HRTIME();
ebb1c51998-02-24Per Hedbor #endif
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor constant decode=roxen->decode; constant find_supports=roxen->find_supports; constant version=roxen->version; constant handle=roxen->handle; constant _query=roxen->query;
e5bad21998-02-10Per Hedbor constant thepipe = roxen->pipe;
ebb1c51998-02-24Per Hedbor constant _time=predef::time;
b1fca01996-11-12Per Hedbor 
7ee5651996-12-10Per Hedbor private static array(string) cache; private static int wanted_data, have_data;
b1fca01996-11-12Per Hedbor object conf; #include <roxen.h> #include <module.h> #undef QUERY
2944901998-01-20Henrik Grubbström (Grubba) #if constant(cpp) #define QUERY(X) _query( #X ) #else /* !constant(cpp) */ #define QUERY(X) _query("X") #endif /* constant(cpp) */
b1fca01996-11-12Per Hedbor 
ec63151998-03-16Francesco Chemolli int time = predef::time(1);
b1fca01996-11-12Per Hedbor string raw_url;
d7b0871997-08-31Per Hedbor int do_not_disconnect;
b1fca01996-11-12Per Hedbor  mapping (string:string) variables = ([ ]); mapping (string:mixed) misc = ([ ]);
ebb1c51998-02-24Per Hedbor mapping (string:string) cookies = ([ ]);
b1fca01996-11-12Per Hedbor  multiset (string) prestate = (< >); multiset (string) config = (< >);
ebb1c51998-02-24Per Hedbor multiset (string) supports; multiset (string) pragma = (< >);
b1fca01996-11-12Per Hedbor  string remoteaddr, host;
ebb1c51998-02-24Per Hedbor array (string) client; array (string) referer;
b1fca01996-11-12Per Hedbor 
beaca01998-02-20Per Hedbor mapping file;
b1fca01996-11-12Per Hedbor  object my_fd; /* The client. */ object pipe; // string range; string prot;
1afe371997-06-12Henrik Grubbström (Grubba) string clientprot;
b1fca01996-11-12Per Hedbor string method; string realfile, virtfile; string rest_query=""; string raw; string query; string not_query; string extra_extension = ""; // special hack for the language module
ebb1c51998-02-24Per Hedbor string data, leftovers;
b1fca01996-11-12Per Hedbor array (int|string) auth; string rawauth, realauth; string since; // Parse a HTTP/1.1 HTTP/1.0 or 0.9 request, including form data and // state variables. Return 0 if more is expected, 1 if done, and -1 // if fatal error.
ebb1c51998-02-24Per Hedbor void end(string|void a,int|void b);
b1fca01996-11-12Per Hedbor 
e5bad21998-02-10Per Hedbor private void setup_pipe()
b1fca01996-11-12Per Hedbor {
e5bad21998-02-10Per Hedbor  if(!my_fd) {
1842611997-05-30Henrik Grubbström (Grubba)  end(); return; }
e5bad21998-02-10Per Hedbor  if(!pipe) pipe=thepipe();
b1fca01996-11-12Per Hedbor }
e5bad21998-02-10Per Hedbor void send(string|object what, int|void len)
b1fca01996-11-12Per Hedbor { if(!what) return;
e5bad21998-02-10Per Hedbor  if(!pipe) setup_pipe(); if(!pipe) return;
b1fca01996-11-12Per Hedbor  if(stringp(what)) pipe->write(what);
e5bad21998-02-10Per Hedbor  else pipe->input(what,len);
b1fca01996-11-12Per Hedbor } string scan_for_query( string f ) { if(sscanf(f,"%s?%s", f, query) == 2) { string v, a, b; foreach(query / "&", v) if(sscanf(v, "%s=%s", a, b) == 2) { a = http_decode_string(replace(a, "+", " ")); b = http_decode_string(replace(b, "+", " ")); if(variables[ a ]) variables[ a ] += "\0" + b; else variables[ a ] = b; } else if(strlen( rest_query )) rest_query += "&" + http_decode_string( v ); else rest_query = http_decode_string( v );
ebb1c51998-02-24Per Hedbor  rest_query=replace(rest_query, "+", "\000"); /* IDIOTIC STUPID STANDARD */
b1fca01996-11-12Per Hedbor  } return f; } private int really_set_config(array mod_config) { string url, m; string base;
14179b1997-01-29Per Hedbor  base = conf->query("MyWorldLocation")||"/";
b1fca01996-11-12Per Hedbor  if(supports->cookies) { #ifdef REQUEST_DEBUG perror("Setting cookie..\n"); #endif if(mod_config) foreach(mod_config, m) if(m[-1]=='-') config[m[1..]]=0; else config[m]=1; if(sscanf(replace(raw_url,({"%3c","%3e","%3C","%3E" }), ({"<",">","<",">"})),"/<%*s>/%s",url)!=2) url = "/";
537ec81997-11-12David Hedbor  if ((base[-1] == '/') && (strlen(url) && url[0] == '/')) {
0ddff61997-07-20Henrik Grubbström (Grubba)  url = base + url[1..]; } else { url = base + url; } my_fd->write(prot + " 302 Config in cookie!\r\n"
b1fca01996-11-12Per Hedbor  "Set-Cookie: "
0ddff61997-07-20Henrik Grubbström (Grubba)  + http_roxen_config_cookie(indices(config) * ",") + "\r\n" "Location: " + url + "\r\n"
b1fca01996-11-12Per Hedbor  "Content-Type: text/html\r\n" "Content-Length: 0\r\n\r\n"); } else { #ifdef REQUEST_DEBUG perror("Setting {config} for user without Cookie support..\n"); #endif if(mod_config) foreach(mod_config, m) if(m[-1]=='-') prestate[m[1..]]=0; else prestate[m]=1;
0ddff61997-07-20Henrik Grubbström (Grubba)  if (sscanf(replace(raw_url, ({ "%3c", "%3e", "%3C", "%3E" }), ({ "<", ">", "<", ">" })), "/<%*s>/%s", url) == 2) { url = "/" + url; } if (sscanf(replace(url, ({ "%28", "%29" }), ({ "(", ")" })), "/(%*s)/%s", url) == 2) { url = "/" + url; } url = add_pre_state(url, prestate); if (base[-1] == '/') { url = base + url[1..]; } else { url = base + url; } my_fd->write(prot + " 302 Config In Prestate!\r\n" "\r\nLocation: " + url + "\r\n" "Content-Type: text/html\r\n" "Content-Length: 0\r\n\r\n");
b1fca01996-11-12Per Hedbor  } return -2; }
0a635b1998-03-08Henrik Grubbström (Grubba) private static mixed f, line;
b1fca01996-11-12Per Hedbor private int parse_got(string s) { multiset (string) sup; array mod_config;
ebb1c51998-02-24Per Hedbor  string a, b, linename, contents;
b1fca01996-11-12Per Hedbor  int config_in_url;
ebb1c51998-02-24Per Hedbor 
ceb9271997-05-15David Hedbor // roxen->httpobjects[my_id] = "Parsed data...";
b1fca01996-11-12Per Hedbor  raw = s;
ebb1c51998-02-24Per Hedbor 
0a635b1998-03-08Henrik Grubbström (Grubba)  if (!line) { int start = search(s, "\r\n"); if ((< -1, 0 >)[start]) { // Not enough data, or malformed request. return ([ -1:0, 0:2 ])[start]; } line = s[..start-1]; // Parse the command start = search(line, " "); if (start != -1) { method = upper_case(line[..start-1]); int end = search(reverse(line[start+1..]), " "); if (end != -1) { f = line[start+1..sizeof(line)-(end+2)]; clientprot = line[sizeof(line)-end..]; if (clientprot != "HTTP/0.9") {
1afe371997-06-12Henrik Grubbström (Grubba)  prot = "HTTP/1.0";
0a635b1998-03-08Henrik Grubbström (Grubba)  // Check that the request is complete int end; if ((end = search(s, "\r\n\r\n")) == -1) { // No, we need more data. return 0; } data = s[end+4..]; s = s[sizeof(line)+2..end-1]; } else { prot = clientprot; data = s[sizeof(line)+2..]; s = ""; // No headers. } } else { f = line[start+1..]; prot = clientprot = "HTTP/0.9"; data = s[sizeof(line)+2..]; s = ""; // No headers.
1afe371997-06-12Henrik Grubbström (Grubba)  }
0a635b1998-03-08Henrik Grubbström (Grubba)  } else { method = upper_case(line); f = "/"; prot = clientprot = "HTTP/0.9"; } } else { // HTTP/1.0 or later // Check that the request is complete int end; if ((end = search(s, "\r\n\r\n")) == -1) { // No, we still need more data. return 0;
1afe371997-06-12Henrik Grubbström (Grubba)  }
0a635b1998-03-08Henrik Grubbström (Grubba)  data = s[end+4..]; s = s[sizeof(line)+2..end-1];
1afe371997-06-12Henrik Grubbström (Grubba)  }
0a635b1998-03-08Henrik Grubbström (Grubba) 
b1fca01996-11-12Per Hedbor  if(method == "PING") { my_fd->write("PONG\n"); return -2; } raw_url = f; time = _time(1);
b04f071997-01-29Per Hedbor 
7ec6601997-01-29Per Hedbor 
b1fca01996-11-12Per Hedbor  if(!remoteaddr) {
2154ca1997-08-31Per Hedbor  if(my_fd) catch(remoteaddr = ((my_fd->query_address()||"")/" ")[0]);
e5bad21998-02-10Per Hedbor  if(!remoteaddr) { end(); return 0; }
b1fca01996-11-12Per Hedbor  }
7ec6601997-01-29Per Hedbor 
b1fca01996-11-12Per Hedbor  f = scan_for_query( f );
06583f1997-09-03Per Hedbor // f = http_decode_string( f );
b1fca01996-11-12Per Hedbor 
e5bad21998-02-10Per Hedbor  if (sscanf(f, "/<%s>/%s", a, f)==2)
b1fca01996-11-12Per Hedbor  { config_in_url = 1; mod_config = (a/",");
e5bad21998-02-10Per Hedbor  f = "/"+f;
b1fca01996-11-12Per Hedbor  }
e5bad21998-02-10Per Hedbor  if ((sscanf(f, "/(%s)/%s", a, f)==2) && strlen(a)) {
b1fca01996-11-12Per Hedbor  prestate = aggregate_multiset(@(a/","-({""})));
e5bad21998-02-10Per Hedbor  f = "/"+f; }
b1fca01996-11-12Per Hedbor 
86729e1997-09-06Per Hedbor  not_query = simplify_path(http_decode_string(f));
b1fca01996-11-12Per Hedbor 
0a635b1998-03-08Henrik Grubbström (Grubba)  if(sizeof(s)) { // sscanf(s, "%s\r\n\r\n%s", s, data);
b1fca01996-11-12Per Hedbor 
ebb1c51998-02-24Per Hedbor // s = replace(s, "\n\t", ", ") - "\r"; // Handle rfc822 continuation lines and strip \r
b1fca01996-11-12Per Hedbor 
ebb1c51998-02-24Per Hedbor  foreach(s/"\r\n" - ({ "" }), line)
b1fca01996-11-12Per Hedbor  { linename=contents=0; sscanf(line, "%s:%s", linename, contents); if(linename&&contents) { linename=lower_case(linename); sscanf(contents, "%*[\t ]%s", contents); if(strlen(contents)) {
48633b1997-08-04Henrik Grubbström (Grubba)  switch (linename) {
ebb1c51998-02-24Per Hedbor  case "content-length":
b1fca01996-11-12Per Hedbor  misc->len = (int)(contents-" ");
ebb1c51998-02-24Per Hedbor  if(!misc->len) continue;
b1fca01996-11-12Per Hedbor  if(method == "POST") {
dcc4351997-05-20Per Hedbor  if(!data) data="";
ebb1c51998-02-24Per Hedbor  int l = misc->len-1; /* Length - 1 */
7ee5651996-12-10Per Hedbor  wanted_data=l; have_data=strlen(data);
ceb9271997-05-15David Hedbor  if(strlen(data) <= l) // \r are included. {
7ee5651996-12-10Per Hedbor  return 0;
ceb9271997-05-15David Hedbor  }
ebb1c51998-02-24Per Hedbor  leftovers = data[l+1..];
7ee5651996-12-10Per Hedbor  data = data[..l]; switch(lower_case(((misc["content-type"]||"")/";")[0]-" "))
86c5101997-10-05Henrik Grubbström (Grubba)  { default: // Normal form data.
7ee5651996-12-10Per Hedbor  string v; if(l < 200000) { foreach(replace(data-"\n", "+", " ")/"&", v) if(sscanf(v, "%s=%s", a, b) == 2) { a = http_decode_string( a ); b = http_decode_string( b );
b1fca01996-11-12Per Hedbor 
7ee5651996-12-10Per Hedbor  if(variables[ a ]) variables[ a ] += "\0" + b; else variables[ a ] = b; } } break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "multipart/form-data":
7ee5651996-12-10Per Hedbor // perror("Multipart/form-data post detected\n");
57d41d1997-08-14Marcus Comstedt  object messg = MIME.Message(data, misc); foreach(messg->body_parts, object part) { if(part->disp_params->filename) { variables[part->disp_params->name]=part->getdata(); variables[part->disp_params->name+".filename"]= part->disp_params->filename; if(!misc->files) misc->files = ({ part->disp_params->name }); else misc->files += ({ part->disp_params->name }); } else { variables[part->disp_params->name]=part->getdata();
7ee5651996-12-10Per Hedbor  } } break; } } break;
b1fca01996-11-12Per Hedbor 
48633b1997-08-04Henrik Grubbström (Grubba)  case "authorization":
b1fca01996-11-12Per Hedbor  string *y; rawauth = contents; y = contents /= " "; if(sizeof(y) < 2) break; y[1] = decode(y[1]); realauth=y[1]; if(conf && conf->auth_module) y = conf->auth_module->auth( y, this_object() ); auth=y; break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "proxy-authorization":
b1fca01996-11-12Per Hedbor  string *y; y = contents /= " "; if(sizeof(y) < 2) break; y[1] = decode(y[1]); if(conf && conf->auth_module) y = conf->auth_module->auth( y, this_object() ); misc->proxyauth=y; break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "pragma":
5e89211997-02-13Per Hedbor  pragma|=aggregate_multiset(@replace(contents, " ", "")/ ",");
b1fca01996-11-12Per Hedbor  break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "user-agent":
ebb1c51998-02-24Per Hedbor  if(!client) { sscanf(contents, "%s via", contents); client = contents/" " - ({ "" }); }
b1fca01996-11-12Per Hedbor  break;
695bba1997-03-22Henrik Grubbström (Grubba)  /* Some of M$'s non-standard user-agent info */
48633b1997-08-04Henrik Grubbström (Grubba)  case "ua-pixels": /* Screen resolution */ case "ua-color": /* Color scheme */ case "ua-os": /* OS-name */ case "ua-cpu": /* CPU-type */
ebb1c51998-02-24Per Hedbor  /* None of the above are interresting or useful for us */
48633b1997-08-04Henrik Grubbström (Grubba)  /* IGNORED */
695bba1997-03-22Henrik Grubbström (Grubba)  break;
b1fca01996-11-12Per Hedbor 
48633b1997-08-04Henrik Grubbström (Grubba)  case "referer":
f6d62d1997-03-26Per Hedbor  referer = contents/" "; break;
b1fca01996-11-12Per Hedbor 
ebb1c51998-02-24Per Hedbor  case "extension":
b1fca01996-11-12Per Hedbor #ifdef DEBUG perror("Client extension: "+contents+"\n"); #endif
ebb1c51998-02-24Per Hedbor  case "connection":
b1fca01996-11-12Per Hedbor  contents = lower_case(contents);
ebb1c51998-02-24Per Hedbor  case "content-type":
48633b1997-08-04Henrik Grubbström (Grubba)  misc[linename] = lower_case(contents);
b1fca01996-11-12Per Hedbor  break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "accept": case "accept-encoding": case "accept-charset": case "accept-language": case "session-id": case "message-id": case "from":
b1fca01996-11-12Per Hedbor  if(misc[linename])
5e89211997-02-13Per Hedbor  misc[linename] += (contents-" ") / ",";
b1fca01996-11-12Per Hedbor  else
5e89211997-02-13Per Hedbor  misc[linename] = (contents-" ") / ",";
b1fca01996-11-12Per Hedbor  break; case "cookie": /* This header is quite heavily parsed */ string c; misc->cookies = contents; foreach(contents/";", c) { string name, value; while(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; if(name == "RoxenConfig" && strlen(value)) { array tmpconfig = value/"," + ({ }); string m; if(mod_config && sizeof(mod_config)) foreach(mod_config, m) if(!strlen(m)) { continue; } /* Bug in parser force { and } */ else if(m[0]=='-') tmpconfig -= ({ m[1..] }); else tmpconfig |= ({ m }); mod_config = 0; config = aggregate_multiset(@tmpconfig); } } } break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "host": case "proxy-connection": case "security-scheme":
b1fca01996-11-12Per Hedbor  misc[linename] = contents; break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "proxy-by": case "proxy-maintainer": case "proxy-software": case "mime-version":
b1fca01996-11-12Per Hedbor  break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "if-modified-since":
ebb1c51998-02-24Per Hedbor  since=contents;
b1fca01996-11-12Per Hedbor  break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "via": case "cache-control":
ebb1c51998-02-24Per Hedbor  case "negotiate":
48633b1997-08-04Henrik Grubbström (Grubba)  case "forwarded":
ebb1c51998-02-24Per Hedbor  misc[linename]=contents;
b1fca01996-11-12Per Hedbor  break;
ebb1c51998-02-24Per Hedbor 
b1fca01996-11-12Per Hedbor #ifdef DEBUG
48633b1997-08-04Henrik Grubbström (Grubba)  default:
b1fca01996-11-12Per Hedbor  /* x-* headers are experimental. */ if(linename[0] != 'x') perror("Unknown header: `"+linename+"' -> `"+contents+"'\n"); #endif } } } } }
ebb1c51998-02-24Per Hedbor  if(!client) client = ({ "unknown" }); if(!supports) supports = find_supports(lower_case(client*" ")); if(!referer) referer = ({ });
dde9651996-12-08David 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";
b1fca01996-11-12Per Hedbor  }
ceb9271997-05-15David Hedbor  if(config_in_url) { return really_set_config( mod_config ); }
b1fca01996-11-12Per Hedbor  if(!supports->cookies) config = prestate; else if(conf
ebb1c51998-02-24Per Hedbor  && QUERY(set_cookie)
27b0e11996-11-26Per Hedbor  && !cookies->RoxenUserID && strlen(not_query)
ebb1c51998-02-24Per Hedbor  && not_query[0]=='/' && method!="PUT")
b1fca01996-11-12Per Hedbor  {
a773c61997-07-06Henrik Grubbström (Grubba)  if (!(QUERY(set_cookie_only_once) && cache_lookup("hosts_for_cookie",remoteaddr))) { misc->moreheads = ([ "Set-Cookie":http_roxen_id_cookie(), ]); } if (QUERY(set_cookie_only_once)) cache_set("hosts_for_cookie",remoteaddr,1);
b1fca01996-11-12Per Hedbor  } return 1; // Done. } void disconnect() {
ebb1c51998-02-24Per Hedbor  if(do_not_disconnect) return;
a37f781998-02-27Per Hedbor  catch(file && file->close());
ebb1c51998-02-24Per Hedbor  file = 0;
e8790b1998-02-19Per Hedbor  my_fd = 0;
14179b1997-01-29Per Hedbor  destruct();
b1fca01996-11-12Per Hedbor }
ebb1c51998-02-24Per Hedbor void end(string|void s, int|void keepit)
b1fca01996-11-12Per Hedbor {
8afc811998-02-04Per Hedbor #ifdef PROFILE if(conf) { float elapsed = SECHR(HRTIME()-req_time); array p; if(!(p=conf->profile_map[not_query])) p = conf->profile_map[not_query] = ({0,0.0,0.0}); conf->profile_map[not_query][0]++; p[1] += elapsed; if(elapsed > p[2]) p[2]=elapsed; }
ceb9271997-05-15David Hedbor #endif
ebb1c51998-02-24Per Hedbor  #ifdef KEEP_ALIVE if(keepit && (!(file->raw || file->len<=0)) && (misc->connection || (prot == "HTTP/1.1")) && my_fd) { // Now.. Transfer control to a new http-object. Reset all variables etc.. object o = object_program(this_object())(); o->remoteaddr = remoteaddr; o->supports = supports; o->host = host; o->client = client; object fd = my_fd; my_fd=0; o->chain(fd,conf,leftovers); disconnect(); return; } #endif
e8790b1998-02-19Per Hedbor  if(objectp(my_fd)) {
ebb1c51998-02-24Per Hedbor  catch { my_fd->set_close_callback(0); my_fd->set_read_callback(0);
a37f781998-02-27Per Hedbor // my_fd->set_blocking();
ebb1c51998-02-24Per Hedbor  if(s) my_fd->write(s);
e8790b1998-02-19Per Hedbor  my_fd->close(); destruct(my_fd);
ebb1c51998-02-24Per Hedbor  };
e8790b1998-02-19Per Hedbor  my_fd = 0; } disconnect();
b1fca01996-11-12Per Hedbor }
ceb9271997-05-15David Hedbor static void do_timeout(mapping foo)
b1fca01996-11-12Per Hedbor {
ebb1c51998-02-24Per Hedbor  int elapsed = _time()-time;
ec63151998-03-16Francesco Chemolli  if(elapsed >= 30)
8afc811998-02-04Per Hedbor  { end("HTTP/1.0 408 Timeout\r\n" "Content-type: text/plain\r\n" "Server: Roxen Challenger\r\n" "\r\n" "Your connection timed out.\n" "Please try again.\n"); } else {
ebb1c51998-02-24Per Hedbor  // premature call_out... *¤#!"
8afc811998-02-04Per Hedbor  call_out(do_timeout, 10); }
b1fca01996-11-12Per Hedbor } mapping internal_error(array err) {
f788481998-03-18Henrik Grubbström (Grubba)  if(QUERY(show_internals)) {
ec63151998-03-16Francesco Chemolli  array(string) bt = (describe_backtrace(err)/"\n") - ({""}); file = http_low_answer(500, sprintf("<h1>Error: Internal server error.</h1>" "<pre><font size=+1>%s</font></pre>" "&lt;backtrace&gt;<ol><li>%s</ol>\n", bt[..1]*"\n", bt[2..]*"<li>"));
f788481998-03-18Henrik Grubbström (Grubba)  } else {
b1fca01996-11-12Per Hedbor  file = http_low_answer(500, "<h1>Error: The server failed to " "fulfill your query.</h1>");
f788481998-03-18Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor  report_error("Internal server error: " + describe_backtrace(err) + "\n"); }
23a7891996-12-15Per Hedbor int wants_more() { return !!cache; }
fc2fcf1997-04-13Per Hedbor constant errors = ([ 200:"200 OK", 201:"201 URI follows", 202:"202 Accepted", 203:"203 Provisional Information", 204:"204 No Content", 300:"300 Moved", 301:"301 Permanent Relocation", 302:"302 Temporary Relocation", 303:"303 Temporary Relocation method and URI", 304:"304 Not Modified", 400:"400 Bad Request", 401:"401 Access denied", 402:"402 Payment Required", 403:"403 Forbidden", 404:"404 No such file or directory.", 405:"405 Method not allowed", 407:"407 Proxy authorization needed", 408:"408 Request timeout", 409:"409 Conflict", 410:"410 This document is no more. It has gone to meet it's creator. It is gone. It will not be coming back. Give up. I promise. There is no such file or directory.", 500:"500 Internal Server Error.", 501:"501 Not Implemented", 502:"502 Gateway Timeout", 503:"503 Service unavailable", ]);
d7b0871997-08-31Per Hedbor 
beaca01998-02-20Per Hedbor void do_log()
d7b0871997-08-31Per Hedbor { if(conf) { int len;
ebb1c51998-02-24Per Hedbor  if(pipe) file->len = pipe->bytes_sent();
d7b0871997-08-31Per Hedbor  if(conf) { if(file->len > 0) conf->sent+=file->len;
f7d9811997-09-12Per Hedbor  conf->log(file, this_object());
d7b0871997-08-31Per Hedbor  } }
ebb1c51998-02-24Per Hedbor  end("",1);
e5bad21998-02-10Per Hedbor  return;
d7b0871997-08-31Per Hedbor }
9f46de1997-04-08Per Hedbor void handle_request( )
b1fca01996-11-12Per Hedbor { mixed *err;
5bc1991997-01-29Per Hedbor  int tmp;
b1fca01996-11-12Per Hedbor  function funp; mapping heads;
5bc1991997-01-29Per Hedbor  string head_string;
14179b1997-01-29Per Hedbor  object thiso=this_object();
5bc1991997-01-29Per Hedbor 
ceb9271997-05-15David Hedbor  remove_call_out(do_timeout);
82f5191997-03-02Per Hedbor 
b1fca01996-11-12Per Hedbor  if(conf) {
14179b1997-01-29Per Hedbor // perror("Handle request, got conf.\n");
68e0301997-08-19Per Hedbor  object oc = conf;
ebb1c51998-02-24Per Hedbor  foreach(conf->first_modules(), funp) {
68e0301997-08-19Per Hedbor  if(file = funp( thiso)) break;
ebb1c51998-02-24Per Hedbor  if(conf != oc) { handle_request(); return; }
68e0301997-08-19Per Hedbor  }
14179b1997-01-29Per Hedbor  if(!file) err=catch(file = conf->get_file(thiso));
b1fca01996-11-12Per Hedbor  if(err) internal_error(err);
ebb1c51998-02-24Per Hedbor 
b1fca01996-11-12Per Hedbor  if(!mappingp(file))
14179b1997-01-29Per Hedbor  foreach(conf->last_modules(), funp) if(file = funp(thiso)) break; } else if(err=catch(file = roxen->configuration_parse( thiso ))) { if(err==-1) return; internal_error(err); }
82f5191997-03-02Per Hedbor 
b1fca01996-11-12Per Hedbor  if(!mappingp(file)) {
f6d62d1997-03-26Per Hedbor  if(misc->error_code) file = http_low_answer(misc->error_code, errors[misc->error]); else if(method != "GET" && method != "HEAD" && method != "POST")
b1fca01996-11-12Per Hedbor  file = http_low_answer(501, "Not implemented."); else
4aaa821998-02-04Per Hedbor  if(catch { file=http_low_answer(404, replace(parse_rxml(conf->query("ZNoSuchFile"), thiso), ({"$File", "$Me"}), ({not_query, conf->query("MyWorldLocation")})));}) internal_error(err);
14179b1997-01-29Per Hedbor  } else { if((file->file == -1) || file->leave_me)
b1fca01996-11-12Per Hedbor  {
fc2fcf1997-04-13Per Hedbor  if(do_not_disconnect) return;
d336db1998-03-11David Hedbor  my_fd = 0; objectp(file) && file->close(); file = 0;
14179b1997-01-29Per Hedbor  return;
b1fca01996-11-12Per Hedbor  }
ebb1c51998-02-24Per Hedbor  if(file->type == "raw") file->raw = 1; else if(!file->type) file->type="text/plain";
14179b1997-01-29Per Hedbor  }
b1fca01996-11-12Per Hedbor  if(!file->raw && prot != "HTTP/0.9") { string h; heads=
ebb1c51998-02-24Per Hedbor  (["MIME-Version":(file["mime-version"] || "1.0"),
b1fca01996-11-12Per Hedbor  "Content-type":file["type"],
ec6a431997-09-19Henrik Grubbström (Grubba)  "Server":replace(version(), " ", "·"),
ebb1c51998-02-24Per Hedbor  "Date":http_date(time) ]);
b1fca01996-11-12Per Hedbor  if(file->encoding) heads["Content-Encoding"] = file->encoding; if(!file->error) file->error=200; if(file->expires) heads->Expires = http_date(file->expires); if(!file->len) { if(objectp(file->file))
14179b1997-01-29Per Hedbor  if(!file->stat && !(file->stat=misc->stat))
b1fca01996-11-12Per Hedbor  file->stat = (int *)file->file->stat(); array fstat; if(arrayp(fstat = file->stat)) { if(file->file && !file->len) file->len = fstat[1]; heads["Last-Modified"] = http_date(fstat[3]); if(since) { if(is_modified(since, fstat[3], fstat[1])) { file->error = 304;
ebb1c51998-02-24Per Hedbor  file->file = 0; file->data=""; // method="";
b1fca01996-11-12Per Hedbor  } } } if(stringp(file->data)) file->len += strlen(file->data); }
86c5101997-10-05Henrik Grubbström (Grubba)  if(mappingp(file->extra_heads)) {
b1fca01996-11-12Per Hedbor  heads |= file->extra_heads;
86c5101997-10-05Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor 
86c5101997-10-05Henrik Grubbström (Grubba)  if(mappingp(misc->moreheads)) {
b1fca01996-11-12Per Hedbor  heads |= misc->moreheads;
86c5101997-10-05Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor 
14179b1997-01-29Per Hedbor  array myheads = ({prot+" "+(file->rettext||errors[file->error])});
b1fca01996-11-12Per Hedbor  foreach(indices(heads), h) if(arrayp(heads[h])) foreach(heads[h], tmp)
5e89211997-02-13Per Hedbor  myheads += ({ `+(h,": ", tmp)});
b1fca01996-11-12Per Hedbor  else
5e89211997-02-13Per Hedbor  myheads += ({ `+(h, ": ", heads[h])});
b1fca01996-11-12Per Hedbor  if(file->len > -1)
14179b1997-01-29Per Hedbor  myheads += ({"Content-length: " + file->len });
a37f781998-02-27Per Hedbor #ifdef KEEP_ALIVE myheads += ({ "Connection: Keep-Alive" }); #endif
14179b1997-01-29Per Hedbor  head_string = (myheads+({"",""}))*"\r\n";
b1fca01996-11-12Per Hedbor 
d7b0871997-08-31Per Hedbor  if(conf) conf->hsent+=strlen(head_string||"");
14179b1997-01-29Per Hedbor  }
ebb1c51998-02-24Per Hedbor  if(method == "HEAD") { file->file = 0; file->data=""; } if(!leftovers) leftovers = data||""; if(file->len > 0 && file->len < 2000)
beaca01998-02-20Per Hedbor  { my_fd->write(head_string + (file->file?file->file->read():file->data)); do_log(); return; }
ebb1c51998-02-24Per Hedbor 
b1fca01996-11-12Per Hedbor  if(head_string) send(head_string);
ebb1c51998-02-24Per Hedbor 
e5bad21998-02-10Per Hedbor  if(method != "HEAD")
b1fca01996-11-12Per Hedbor  {
ebb1c51998-02-24Per Hedbor  if(file->data && strlen(file->data)) send(file->data, file->len); if(file->file) send(file->file, file->len);
e5bad21998-02-10Per Hedbor  }
0a635b1998-03-08Henrik Grubbström (Grubba)  if (pipe) { pipe->set_done_callback( do_log ); pipe->output(my_fd); } else { my_fd->close(); do_log(); }
14179b1997-01-29Per Hedbor } /* We got some data on a socket. * ================================================= */
ebb1c51998-02-24Per Hedbor int processed;
14179b1997-01-29Per Hedbor void got_data(mixed fooid, string s) { int tmp;
ceb9271997-05-15David Hedbor  remove_call_out(do_timeout);
2154ca1997-08-31Per Hedbor  call_out(do_timeout, 30); // Close down if we don't get more data
ceb9271997-05-15David Hedbor  // within 30 seconds. Should be more than enough.
14179b1997-01-29Per Hedbor  if(wanted_data) { if(strlen(s)+have_data < wanted_data) { cache += ({ s }); have_data += strlen(s); return; } } if(cache) {
ebb1c51998-02-24Per Hedbor  s = cache*""+s;
14179b1997-01-29Per Hedbor  cache = 0; }
ebb1c51998-02-24Per Hedbor  sscanf(s, "%*[\n\r]%s", s); if(strlen(s)) tmp = parse_got(s);
14179b1997-01-29Per Hedbor  switch(-tmp) { case 0: cache = ({ s }); // More on the way. return; case 1: end(prot+" 500 Stupid Client Error\r\nContent-Length: 0\r\n\r\n"); return; // Stupid request. case 2: end(); return; }
ebb1c51998-02-24Per Hedbor 
14179b1997-01-29Per Hedbor  if(conf) { conf->received += strlen(s); conf->requests++; }
2154ca1997-08-31Per Hedbor  my_fd->set_close_callback(0); my_fd->set_read_callback(0);
a37f781998-02-27Per Hedbor // my_fd->set_blocking();
ebb1c51998-02-24Per Hedbor  processed=1;
14179b1997-01-29Per Hedbor #ifdef THREADS
e5bad21998-02-10Per Hedbor  roxen->handle(this_object()->handle_request);
14179b1997-01-29Per Hedbor #else
e5bad21998-02-10Per Hedbor  handle_request();
b1fca01996-11-12Per Hedbor #endif } /* Get a somewhat identical copy of this object, used when doing * 'simulated' requests. */ object clone_me() {
14179b1997-01-29Per Hedbor  object c,t; c=object_program(t=this_object())();
b1fca01996-11-12Per Hedbor 
ebb1c51998-02-24Per Hedbor // c->first = first;
b1fca01996-11-12Per Hedbor  c->conf = conf; c->time = time;
f6d62d1997-03-26Per Hedbor  c->raw_url = raw_url; // c->do_not_disconnect = do_not_disconnect; // No use where there is no fd.. c->variables = copy_value(variables);
48fa361997-04-05Per Hedbor  c->misc = copy_value(misc); c->misc->orig = t;
f6d62d1997-03-26Per Hedbor 
b1fca01996-11-12Per Hedbor  c->prestate = prestate; c->supports = supports;
f6d62d1997-03-26Per Hedbor  c->config = config;
b1fca01996-11-12Per Hedbor  c->remoteaddr = remoteaddr;
f6d62d1997-03-26Per Hedbor  c->host = host;
b1fca01996-11-12Per Hedbor  c->client = client;
f6d62d1997-03-26Per Hedbor  c->referer = referer; c->pragma = pragma; c->cookies = cookies; // file.. c->my_fd = 0; // pipe..
ebb1c51998-02-24Per Hedbor 
f6d62d1997-03-26Per Hedbor  c->prot = prot;
1afe371997-06-12Henrik Grubbström (Grubba)  c->clientprot = clientprot;
f6d62d1997-03-26Per Hedbor  c->method = method; // realfile virtfile // Should not be copied. c->rest_query = rest_query; c->raw = raw; c->query = query; c->not_query = not_query; c->extra_extension = extra_extension; c->data = data;
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() { if(!(my_fd && objectp(my_fd))) end(); else if((_time(1) - time) > 4800) end(); }
14179b1997-01-29Per Hedbor void create(object f, object c)
b1fca01996-11-12Per Hedbor {
14179b1997-01-29Per Hedbor  if(f) { my_fd = f; conf = c; mark_fd(my_fd->query_fd(), "HTTP connection");
beaca01998-02-20Per Hedbor  my_fd->set_close_callback(end); my_fd->set_read_callback(got_data);
ceb9271997-05-15David Hedbor  // No need to wait more than 30 seconds to get more data.
3158d21997-12-10David Hedbor  call_out(do_timeout, 30);
14179b1997-01-29Per Hedbor  }
b1fca01996-11-12Per Hedbor }
ceb9271997-05-15David Hedbor 
ebb1c51998-02-24Per Hedbor void chain(object f, object c, string le) { my_fd = f; conf = c; do_not_disconnect=-1; if(strlen(le)) got_data(0,le); if(!my_fd) { if(do_not_disconnect == -1) { do_not_disconnect=0; disconnect(); } } else if(!processed) { f->set_close_callback(end); f->set_read_callback(got_data); } } // void chain(object fd, object conf, string leftovers) // { // call_out(real_chain,0,fd,conf,leftovers); // }