9211b21998-04-24Per Hedbor // This is a roxen module. // Copyright © 1996 - 1998, Idonex AB.
2a2a5b1996-12-01Per Hedbor 
86e77d1998-05-07Per Hedbor #define MAGIC_ERROR #ifdef MAGIC_ERROR inherit "highlight_pike"; #endif
4ee9601999-07-02David Hedbor constant cvs_version = "$Id: http.pike,v 1.138 1999/07/02 20:50:03 neotron 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 
aa92c11998-08-20Henrik Grubbström (Grubba) #ifdef REQUEST_DEBUG #define DPERROR(X) roxen_perror((X)+"\n") #else #define DPERROR(X) #endif
3235691998-03-26Per Hedbor #ifdef FD_DEBUG
aa92c11998-08-20Henrik Grubbström (Grubba) #define MARK_FD(X) catch{DPERROR(X); mark_fd(my_fd->query_fd(), (X)+" "+remoteaddr);}
3235691998-03-26Per Hedbor #else
aa92c11998-08-20Henrik Grubbström (Grubba) #define MARK_FD(X) DPERROR(X)
3235691998-03-26Per Hedbor #endif
9211b21998-04-24Per Hedbor constant decode = MIME.decode_base64;
35c2631999-02-16Per Hedbor constant find_supports = roxen.find_supports; constant version = roxen.version; constant _query = roxen.query; constant thepipe = roxen.pipe;
9211b21998-04-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 
9211b21998-04-24Per Hedbor int time;
b1fca01996-11-12Per Hedbor string raw_url;
d7b0871997-08-31Per Hedbor int do_not_disconnect;
8200cd1998-07-14Henrik Grubbström (Grubba) mapping (string:string) variables = ([ ]); mapping (string:mixed) misc = ([ ]); mapping (string:string) cookies = ([ ]); mapping (string:string) request_headers = ([ ]);
b1fca01996-11-12Per Hedbor 
06d5331998-04-03Per Hedbor multiset (string) prestate = (< >); multiset (string) config = (< >); multiset (string) supports = (< >);
ebb1c51998-02-24Per Hedbor multiset (string) pragma = (< >);
b1fca01996-11-12Per Hedbor  string remoteaddr, host;
ebb1c51998-02-24Per Hedbor array (string) client; array (string) referer;
b1fca01996-11-12Per Hedbor 
beaca01998-02-20Per Hedbor mapping file;
b1fca01996-11-12Per Hedbor  object my_fd; /* The client. */ string prot;
1afe371997-06-12Henrik Grubbström (Grubba) string clientprot;
b1fca01996-11-12Per Hedbor string method; string realfile, virtfile; string rest_query=""; string raw; string query; string not_query;
60a1a11998-08-03Henrik Grubbström (Grubba) string extra_extension = ""; // special hack for the language module
ebb1c51998-02-24Per Hedbor string data, leftovers;
b1fca01996-11-12Per Hedbor array (int|string) auth; string rawauth, realauth; string since; // 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 
9d8ea11999-04-16Henrik Grubbström (Grubba) #if constant(Stdio.sendfile) object pipe; // Always 0. static array(string) result_headers = ({}); static object result_file; static int result_f_len; void send(string|object what, int|void len) { if (stringp(what)) { result_headers += ({ what }); } else { if (result_file) { error("HTTP: Multiple result files are not supported!\n"); } result_file = what; result_f_len = len; } }
336ee41999-04-17Henrik Grubbström (Grubba) static void send_done(int bytes, function callback, array(mixed) args)
9d8ea11999-04-16Henrik Grubbström (Grubba) { file->len = bytes;
336ee41999-04-17Henrik Grubbström (Grubba)  callback(@args);
9d8ea11999-04-16Henrik Grubbström (Grubba) }
336ee41999-04-17Henrik Grubbström (Grubba) void start_sender(function callback, mixed ... args)
9d8ea11999-04-16Henrik Grubbström (Grubba) { array(string) headers = result_headers; object file = result_file; int len = result_f_len; result_headers = 0; result_file = 0; result_f_len = 0; // FIXME: Timeout handling!
336ee41999-04-17Henrik Grubbström (Grubba)  Stdio.sendfile(headers, file, -1, len, 0, my_fd, send_done, callback, args);
9d8ea11999-04-16Henrik Grubbström (Grubba) } #else /* !constant(Stdio.sendfile) */ object pipe;
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 {
aa92c11998-08-20Henrik Grubbström (Grubba) #ifdef REQUEST_DEBUG roxen_perror(sprintf("send(%O, %O)\n", what, len)); #endif /* REQUEST_DEBUG */
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 }
336ee41999-04-17Henrik Grubbström (Grubba) void start_sender(function callback, mixed ... args)
9d8ea11999-04-16Henrik Grubbström (Grubba) { if (pipe) { MARK_FD("HTTP really handled, piping "+not_query); #ifdef FD_DEBUG call_out(timer, 30, _time(1)); // Update FD with time... #endif
336ee41999-04-17Henrik Grubbström (Grubba)  // FIXME: What about args? pipe->set_done_callback( callback );
9d8ea11999-04-16Henrik Grubbström (Grubba)  pipe->output(my_fd); } else { MARK_FD("HTTP really handled, pipe done");
336ee41999-04-17Henrik Grubbström (Grubba)  callback(@args);
9d8ea11999-04-16Henrik Grubbström (Grubba)  } } #endif /* constant(Stdio.sendfile) */
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 */
9211b21998-04-24Per Hedbor  }
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;
b5218e1999-03-18Henrik Grubbström (Grubba) private static int hstart;
0a635b1998-03-08Henrik Grubbström (Grubba) 
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 
9d8ea11999-04-16Henrik Grubbström (Grubba)  DPERROR(sprintf("HTTP: parse_got(%O)", s));
b1fca01996-11-12Per Hedbor  raw = s;
ebb1c51998-02-24Per Hedbor 
0a635b1998-03-08Henrik Grubbström (Grubba)  if (!line) {
b5218e1999-03-18Henrik Grubbström (Grubba)  // Used to search for \r\n, but Netscape 4.5 sends just a \n // when doing a proxy-request. // example line: // "CONNECT mikabran:443 HTTP/1.0\n" // "User-Agent: Mozilla/4.5 [en] (X11; U; Linux 2.0.35 i586)" hstart = search(s, "\n"); if ((< -1, 0 >)[hstart]) {
0a635b1998-03-08Henrik Grubbström (Grubba)  // Not enough data, or malformed request.
9d8ea11999-04-16Henrik Grubbström (Grubba)  DPERROR(sprintf("HTTP: parse_got(%O): " "Not enough data, or malformed request.", s));
b5218e1999-03-18Henrik Grubbström (Grubba)  return ([ -1:0, 0:2 ])[hstart]; } if (s[hstart-1] == '\r') { line = s[..hstart-2]; } else { // Kludge for Netscape 4.5 sending bad requests. line = s[..hstart-1];
0a635b1998-03-08Henrik Grubbström (Grubba)  } // Parse the command
b5218e1999-03-18Henrik Grubbström (Grubba)  int start = search(line, " ");
0a635b1998-03-08Henrik Grubbström (Grubba)  if (start != -1) { method = upper_case(line[..start-1]);
d714e61999-03-18Henrik Grubbström (Grubba)  string l = reverse(line[start+1..]); int end;
67d38c1999-03-18Henrik Grubbström (Grubba)  if (!(end = search(l, " "))) {
d714e61999-03-18Henrik Grubbström (Grubba)  // Seems the line has extra spaces at the end. // Get rid of them. sscanf(l, "%*[ ]%s", l); line = line[..sizeof(l)+start];
67d38c1999-03-18Henrik Grubbström (Grubba)  end = search(l, " ");
d714e61999-03-18Henrik Grubbström (Grubba)  }
0a635b1998-03-08Henrik Grubbström (Grubba)  if (end != -1) { f = line[start+1..sizeof(line)-(end+2)];
06d5331998-04-03Per Hedbor  prot = clientprot = line[sizeof(line)-end..];
aec3bb1998-07-03Henrik Grubbström (Grubba)  if (!(< "HTTP/0.9", "HTTP/1.0", "HTTP/1.1" >)[upper_case(prot)]) { if (upper_case(prot)[..3] == "HTTP") { // Latest implemented version of HTTP implemented by this // module is HTTP/1.1. prot = "HTTP/1.1"; } else { // Unknown protocol
4f68de1998-12-19Henrik Grubbström (Grubba)  werror(sprintf("HTTP: Bad request: %O\n", line));
aec3bb1998-07-03Henrik Grubbström (Grubba)  my_fd->write(sprintf("400 Unknown Protocol HTTP/1.1\r\n\r\n"
4f68de1998-12-19Henrik Grubbström (Grubba)  "Protocol %O is not HTTP.\r\n", prot));
aec3bb1998-07-03Henrik Grubbström (Grubba)  return -2; } }
06d5331998-04-03Per Hedbor  // Check that the request is complete int end; if ((end = search(s, "\r\n\r\n")) == -1) { // No, we need more data.
9d8ea11999-04-16Henrik Grubbström (Grubba)  DPERROR("HTTP: parse_got(): Request is not complete.");
06d5331998-04-03Per Hedbor  return 0;
0a635b1998-03-08Henrik Grubbström (Grubba)  }
06d5331998-04-03Per Hedbor  data = s[end+4..];
b5218e1999-03-18Henrik Grubbström (Grubba)  s = s[hstart+1..end-1];
0a635b1998-03-08Henrik Grubbström (Grubba)  } 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.
9d8ea11999-04-16Henrik Grubbström (Grubba)  DPERROR("HTTP: parse_got(): Request is not complete.");
0a635b1998-03-08Henrik Grubbström (Grubba)  return 0;
1afe371997-06-12Henrik Grubbström (Grubba)  }
0a635b1998-03-08Henrik Grubbström (Grubba)  data = s[end+4..];
b5218e1999-03-18Henrik Grubbström (Grubba)  s = s[hstart+1..end-1];
1afe371997-06-12Henrik Grubbström (Grubba)  }
0a635b1998-03-08Henrik Grubbström (Grubba) 
b1fca01996-11-12Per Hedbor  if(method == "PING") {
aec3bb1998-07-03Henrik Grubbström (Grubba)  my_fd->write("PONG\r\n");
b1fca01996-11-12Per Hedbor  return -2; }
019b711999-01-13David Hedbor 
0fa5851999-01-13David Hedbor  if(!(<"CONNECT", "GET", "HEAD", "POST", "PUT", "MOVE", "DELETE">)[method] ) {
019b711999-01-13David Hedbor  send_result(http_low_answer(501, "<title>Method Not Implemented</title>" "\n<h1>Method not implemented.</h1>\n")); return -2; }
b1fca01996-11-12Per Hedbor  raw_url = f; time = _time(1);
b04f071997-01-29Per Hedbor 
fe14f51998-08-26Henrik Grubbström (Grubba)  DPERROR(sprintf("RAW_URL:%O", raw_url));
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) {
9d8ea11999-04-16Henrik Grubbström (Grubba)  DPERROR("HTTP: parse_request(): No remote address.");
e5bad21998-02-10Per Hedbor  end();
9d8ea11999-04-16Henrik Grubbström (Grubba)  return -2;
e5bad21998-02-10Per Hedbor  }
b1fca01996-11-12Per Hedbor  }
7ec6601997-01-29Per Hedbor 
b1fca01996-11-12Per Hedbor  f = scan_for_query( f );
fe14f51998-08-26Henrik Grubbström (Grubba)  DPERROR(sprintf("After query scan:%O", f));
fb5f681998-11-04Peter Bortas  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  }
fe14f51998-08-26Henrik Grubbström (Grubba)  DPERROR(sprintf("After cookie scan:%O", 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; }
fe14f51998-08-26Henrik Grubbström (Grubba)  DPERROR(sprintf("After prestate scan:%O", f));
b1fca01996-11-12Per Hedbor 
fb5f681998-11-04Peter Bortas  not_query = simplify_path(f);
b1fca01996-11-12Per Hedbor 
fe14f51998-08-26Henrik Grubbström (Grubba)  DPERROR(sprintf("After simplify_path == not_query:%O", not_query));
8200cd1998-07-14Henrik Grubbström (Grubba)  request_headers = ([]); // FIXME: KEEP-ALIVE?
0a635b1998-03-08Henrik Grubbström (Grubba)  if(sizeof(s)) { // sscanf(s, "%s\r\n\r\n%s", s, data);
ebb1c51998-02-24Per Hedbor // s = replace(s, "\n\t", ", ") - "\r"; // Handle rfc822 continuation lines and strip \r foreach(s/"\r\n" - ({ "" }), line)
b1fca01996-11-12Per Hedbor  { linename=contents=0; sscanf(line, "%s:%s", linename, contents);
8200cd1998-07-14Henrik Grubbström (Grubba)  if(linename && contents)
b1fca01996-11-12Per Hedbor  { linename=lower_case(linename); sscanf(contents, "%*[\t ]%s", contents);
8200cd1998-07-14Henrik Grubbström (Grubba)  request_headers[linename] = contents;
b1fca01996-11-12Per Hedbor  if(strlen(contents)) {
48633b1997-08-04Henrik Grubbström (Grubba)  switch (linename) {
ebb1c51998-02-24Per Hedbor  case "content-length":
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 
9d8ea11999-04-16Henrik Grubbström (Grubba)  if(strlen(data) <= l) { // \r are included. DPERROR("HTTP: parse_request(): More data needed in POST.");
7ee5651996-12-10Per Hedbor  return 0;
9d8ea11999-04-16Henrik Grubbström (Grubba)  }
9211b21998-04-24Per Hedbor 
ebb1c51998-02-24Per Hedbor  leftovers = data[l+1..];
7ee5651996-12-10Per Hedbor  data = data[..l];
fbfd721998-05-18Henrik Grubbström (Grubba)  switch(lower_case((((misc["content-type"]||"")+";")/";")[0]-" "))
86c5101997-10-05Henrik Grubbström (Grubba)  { default: // Normal form data.
7ee5651996-12-10Per Hedbor  string v; if(l < 200000) {
7a0ee61998-06-27Henrik Grubbström (Grubba)  foreach(replace(data, ({ "\n", "\r", "+" }), ({ "", "", " "}))/"&", v)
7ee5651996-12-10Per Hedbor  if(sscanf(v, "%s=%s", a, b) == 2) { a = http_decode_string( a ); b = http_decode_string( b );
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 {
05a7c81998-08-22Johan Schön  if(variables[part->disp_params->name]) variables[part->disp_params->name] += "\0" + part->getdata(); else variables[part->disp_params->name] = part->getdata();
7ee5651996-12-10Per Hedbor  } } break; } } break;
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 */
727e4c1998-04-12David Hedbor  case "ua-pixels": /* Screen resolution */ case "ua-color": /* Color scheme */ case "ua-os": /* OS-name */ case "ua-cpu": /* CPU-type */ misc[linename - "ua-"] = contents ; 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
d986281999-06-30David Hedbor  case "range": contents = lower_case(contents-" "); if(!search(contents, "bytes")) // Only care about "byte" ranges. misc->range = contents[6..]; break;
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-encoding":
dd23d71998-03-31Henrik Grubbström (Grubba)  foreach((contents-" ")/",", string e) { if (lower_case(e) == "gzip") { supports["autogunzip"] = 1; } } case "accept":
48633b1997-08-04Henrik Grubbström (Grubba)  case "accept-charset": case "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;
5e5d6a1998-04-30Henrik Grubbström (Grubba)  if (!sizeof(contents)) { // Needed for the new Pike 0.6 break; } foreach(((contents/";") - ({""})), c)
b1fca01996-11-12Per Hedbor  { string name, value;
5e5d6a1998-04-30Henrik Grubbström (Grubba)  while(sizeof(c) && c[0]==' ') c=c[1..];
b1fca01996-11-12Per Hedbor  if(sscanf(c, "%s=%s", name, value) == 2) { value=http_decode_string(value); name=http_decode_string(name); cookies[ name ]=value; if(name == "RoxenConfig" && strlen(value)) { array tmpconfig = value/"," + ({ }); string m; if(mod_config && sizeof(mod_config)) foreach(mod_config, m) if(!strlen(m)) { continue; } /* Bug in parser force { and } */ else if(m[0]=='-') tmpconfig -= ({ m[1..] }); else tmpconfig |= ({ m }); mod_config = 0; config = aggregate_multiset(@tmpconfig); } } } break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "host": case "proxy-connection": case "security-scheme":
6e89ab1998-08-10Per Hedbor  case "via": case "cache-control": case "negotiate": case "forwarded":
9b10a41998-10-18Henrik Grubbström (Grubba)  case "new-uri":
6e89ab1998-08-10Per Hedbor  misc[linename]=contents;
b1fca01996-11-12Per Hedbor  break;
48633b1997-08-04Henrik Grubbström (Grubba)  case "proxy-by": case "proxy-maintainer": case "proxy-software": case "mime-version":
b1fca01996-11-12Per Hedbor  break;
4ee9601999-07-02David Hedbor  case "if-modified-since":
ebb1c51998-02-24Per Hedbor  since=contents;
b1fca01996-11-12Per Hedbor  break; } } } } }
ebb1c51998-02-24Per Hedbor  if(!client) client = ({ "unknown" });
64b0501998-04-02David Hedbor  supports = find_supports(lower_case(client*" "), supports);
ebb1c51998-02-24Per Hedbor  if(!referer) referer = ({ });
dde9651996-12-08David Hedbor  if(misc->proxyauth) { // The Proxy-authorization header should be removed... So there. mixed tmp1,tmp2; 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) {
9d8ea11999-04-16Henrik Grubbström (Grubba)  DPERROR("HTTP: parse_got(): config_in_url");
ceb9271997-05-15David Hedbor  return really_set_config( mod_config ); }
b1fca01996-11-12Per Hedbor  if(!supports->cookies) config = prestate; else 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  file = 0;
3235691998-03-26Per Hedbor  MARK_FD("my_fd in HTTP disconnected?");
35ed001998-03-26Per Hedbor  if(do_not_disconnect)return;
14179b1997-01-29Per Hedbor  destruct();
b1fca01996-11-12Per Hedbor }
ebb1c51998-02-24Per Hedbor void end(string|void s, int|void keepit)
b1fca01996-11-12Per Hedbor {
903e571998-03-25Per Hedbor  pipe = 0;
8afc811998-02-04Per Hedbor #ifdef PROFILE if(conf) { float elapsed = SECHR(HRTIME()-req_time);
3117791998-08-20Per Hedbor  string nid = #ifdef FILE_PROFILE not_query #else dirname(not_query) #endif ;
8afc811998-02-04Per Hedbor  array p;
3117791998-08-20Per Hedbor  if(!(p=conf->profile_map[nid])) p = conf->profile_map[nid] = ({0,0.0,0.0});
10f7971999-04-22Per Hedbor  p[0]++;
8afc811998-02-04Per Hedbor  p[1] += elapsed; if(elapsed > p[2]) p[2]=elapsed; }
ceb9271997-05-15David Hedbor #endif
ebb1c51998-02-24Per Hedbor  #ifdef KEEP_ALIVE if(keepit &&
18b3161998-03-25David Hedbor  (!(file->raw || file->len <= 0))
ebb1c51998-02-24Per Hedbor  && (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;
3235691998-03-26Per Hedbor  MARK_FD("HTTP kept alive");
ebb1c51998-02-24Per Hedbor  object fd = my_fd; my_fd=0;
903e571998-03-25Per Hedbor  if(s) leftovers += s;
ebb1c51998-02-24Per Hedbor  o->chain(fd,conf,leftovers); disconnect(); return; } #endif
e8790b1998-02-19Per Hedbor  if(objectp(my_fd)) {
3235691998-03-26Per Hedbor  MARK_FD("HTTP closed");
ebb1c51998-02-24Per Hedbor  catch { my_fd->set_close_callback(0); my_fd->set_read_callback(0);
3235691998-03-26Per Hedbor  my_fd->set_blocking();
ebb1c51998-02-24Per Hedbor  if(s) my_fd->write(s);
e8790b1998-02-19Per Hedbor  my_fd->close(); destruct(my_fd);
ebb1c51998-02-24Per Hedbor  };
e8790b1998-02-19Per Hedbor  my_fd = 0; } disconnect();
b1fca01996-11-12Per Hedbor }
9211b21998-04-24Per Hedbor static void do_timeout()
b1fca01996-11-12Per Hedbor {
beb4b21998-06-02Per Hedbor  // werror("do_timeout() called, time="+time+"; time()="+_time()+"\n");
ebb1c51998-02-24Per Hedbor  int elapsed = _time()-time;
9211b21998-04-24Per Hedbor  if(time && elapsed >= 30)
8afc811998-02-04Per Hedbor  {
3235691998-03-26Per Hedbor  MARK_FD("HTTP timeout");
beb4b21998-06-02Per Hedbor  // Do not under any circumstances send any data as a reply here. // This is an easy reason why: It breaks keep-alive totaly. // It is not a very good idea to do that, since it might be enabled // per deafult any century now.. end("");
8afc811998-02-04Per Hedbor  } else {
ebb1c51998-02-24Per Hedbor  // premature call_out... *¤#!"
8afc811998-02-04Per Hedbor  call_out(do_timeout, 10);
3235691998-03-26Per Hedbor  MARK_FD("HTTP premature timeout");
8afc811998-02-04Per Hedbor  }
b1fca01996-11-12Per Hedbor }
35ed001998-03-26Per Hedbor static string last_id, last_from; string get_id(string from) { if(last_from == from) return last_id; last_from=from; catch { object f = open(from,"r"); string id; id = f->read(200); if(sscanf(id, "%*s$"+"Id: %*s,v %s ", id) == 3)
0b651e1998-03-26Per Hedbor  return last_id=" (version "+id+")";
35ed001998-03-26Per Hedbor  }; last_id = ""; return ""; } void add_id(array to) {
0b651e1998-03-26Per Hedbor  foreach(to[1], array q) if(stringp(q[0])) q[0]+=get_id(q[0]);
35ed001998-03-26Per Hedbor }
86e77d1998-05-07Per Hedbor string link_to(string what, int eid, int qq) { int line; string file, fun; sscanf(what, "%s(%*s in line %d in %s", fun, line, file); if(file && fun && line) { sscanf(file, "%s (", file); if(file[0]!='/') file = combine_path(getcwd(), file); // werror("link to the function "+fun+" in the file "+ // file+" line "+line+"\n"); return ("<a href=\"/(old_error,find_file)/error?"+ "file="+http_encode_string(file)+"&" "fun="+http_encode_string(fun)+"&" "off="+qq+"&" "error="+eid+"&" "line="+line+"#here\">"); } return "<a>"; } string format_backtrace(array bt, int eid)
2b9d4b1998-03-25Per Hedbor { // first entry is always the error, // second is the actual function, // rest is backtrace.
35c2631999-02-16Per Hedbor  string reason = roxen.diagnose_error( bt );
bd22231998-03-27David Hedbor  if(sizeof(bt) == 1) // No backtrace?! bt += ({ "Unknown error, no backtrace."});
2b9d4b1998-03-25Per Hedbor  string res = ("<title>Internal Server Error</title>" "<body bgcolor=white text=black link=darkblue vlink=darkblue>" "<table width=\"100%\" border=0 cellpadding=0 cellspacing=0>" "<tr><td valign=bottom align=left><img border=0 "
86e77d1998-05-07Per Hedbor  "src=\""+(conf?"/internal-roxen-":"/img/")+ "roxen-icon-gray.gif\" alt=\"\"></td>"
2b9d4b1998-03-25Per Hedbor  "<td>&nbsp;</td><td width=100% height=39>" "<table cellpadding=0 cellspacing=0 width=100% border=0>" "<td width=\"100%\" align=right valigh=center height=28>" "<b><font size=+1>Failed to complete your request</font>" "</b></td></tr><tr width=\"100%\"><td bgcolor=\"#003366\" " "align=right height=12 width=\"100%\"><font color=white " "size=-2>Internal Server Error&nbsp;&nbsp;</font></td>"
86e77d1998-05-07Per Hedbor  "</tr></table></td></tr></table>" "<p>\n\n" "<font size=+2 color=darkred>" "<img alt=\"\" hspace=10 align=left src="+ (conf?"/internal-roxen-":"/img/") +"manual-warning.gif>" +bt[0]+"</font><br>\n" "The error occured while calling <b>"+bt[1]+"</b><p>\n" +(reason?reason+"<p>":"") +"<br><h3><br>Complete Backtrace:</h3>\n\n<ol>");
2b9d4b1998-03-25Per Hedbor  int q = sizeof(bt)-1; foreach(bt[1..], string line) {
35ed001998-03-26Per Hedbor  string fun, args, where, fo;
0ce9d21998-05-12Henrik Grubbström (Grubba)  if((sscanf(html_encode_string(line), "%s(%s) in %s", fun, args, where) == 3) &&
721c051998-05-12Johan Schön  (sscanf(where, "%*s in %s", fo) && fo)) {
86e77d1998-05-07Per Hedbor  line += get_id( fo ); res += ("<li value="+(q--)+"> "+
0ce9d21998-05-12Henrik Grubbström (Grubba)  (replace(line, fo, link_to(line,eid,sizeof(bt)-q-1)+fo+"</a>")
86e77d1998-05-07Per Hedbor  -(getcwd()+"/"))+"<p>\n");
0ce9d21998-05-12Henrik Grubbström (Grubba)  } else {
86e77d1998-05-07Per Hedbor  res += "<li value="+(q--)+"> <b><font color=darkgreen>"+ line+"</font></b><p>\n";
0ce9d21998-05-12Henrik Grubbström (Grubba)  }
2b9d4b1998-03-25Per Hedbor  }
86e77d1998-05-07Per Hedbor  res += ("</ul><p><b><a href=\"/(old_error,plain)/error?error="+eid+"\">"
2b9d4b1998-03-25Per Hedbor  "Generate text-only version of this error message, for bug reports"+ "</a></b>"); return res+"</body>"; }
b1fca01996-11-12Per Hedbor 
f288181998-05-25Per Hedbor string generate_bugreport(array from, string u, string rd)
2b9d4b1998-03-25Per Hedbor {
35ed001998-03-26Per Hedbor  add_id(from);
0b651e1998-03-26Per Hedbor  return ("<pre>"+html_encode_string("Roxen version: "+version()+
35c2631999-02-16Per Hedbor  (roxen.real_version != version()? " ("+roxen.real_version+")":"")+
f288181998-05-25Per Hedbor  "\nRequested URL: "+u+"\n"
9a72d61998-03-25Per Hedbor  "\nError: "+
35ed001998-03-26Per Hedbor  describe_backtrace(from)-(getcwd()+"/")+
f288181998-05-25Per Hedbor  "\n\nRequest data:\n"+rd)); } string censor(string what) { string a, b, c; if(sscanf(what, "%shorization:%s\n%s", a, b, c)==3) return a+" ################ (censored)\n"+c; return what;
2b9d4b1998-03-25Per Hedbor }
b1fca01996-11-12Per Hedbor 
86e77d1998-05-07Per Hedbor int store_error(array err) {
35c2631999-02-16Per Hedbor  mapping e = roxen.query_var("errors"); if(!e) roxen.set_var("errors", ([])); e = roxen.query_var("errors"); /* threads... */
86e77d1998-05-07Per Hedbor  int id = ++e[0]; if(id>1024) id = 1;
f288181998-05-25Per Hedbor  e[id] = ({err,raw_url,censor(raw)});
86e77d1998-05-07Per Hedbor  return id; } array get_error(string eid) {
35c2631999-02-16Per Hedbor  mapping e = roxen.query_var("errors");
86e77d1998-05-07Per Hedbor  if(e) return e[(int)eid]; return 0; }
2b9d4b1998-03-25Per Hedbor void internal_error(array err)
b1fca01996-11-12Per Hedbor {
bd22231998-03-27David Hedbor  array err2;
86e77d1998-05-07Per Hedbor  if(QUERY(show_internals)) {
bd22231998-03-27David Hedbor  err2 = catch { array(string) bt = (describe_backtrace(err)/"\n") - ({""});
86e77d1998-05-07Per Hedbor  file = http_low_answer(500, format_backtrace(bt, store_error(err)));
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)); file = http_low_answer(500, "<h1>Error: The server failed to " "fulfill your query, due to an "
86e77d1998-05-07Per Hedbor  "internal error in the internal error routine.</h1>");
2b9d4b1998-03-25Per Hedbor  }
f788481998-03-18Henrik Grubbström (Grubba)  } else {
b1fca01996-11-12Per Hedbor  file = http_low_answer(500, "<h1>Error: The server failed to "
2b9d4b1998-03-25Per Hedbor  "fulfill your query, due to an internal error.</h1>");
f788481998-03-18Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor  report_error("Internal server error: " + describe_backtrace(err) + "\n"); }
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",
d986281999-06-30David Hedbor  206:"206 Partial Content", // Byte ranges
fc2fcf1997-04-13Per Hedbor  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.",
d986281999-06-30David Hedbor  416:"416 Requested range not satisfiable",
fc2fcf1997-04-13Per Hedbor  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 {
3235691998-03-26Per Hedbor  MARK_FD("HTTP logging"); // fd can be closed here
d7b0871997-08-31Per Hedbor  if(conf) { int len;
ebb1c51998-02-24Per Hedbor  if(pipe) file->len = pipe->bytes_sent();
d7b0871997-08-31Per Hedbor  if(conf) { if(file->len > 0) conf->sent+=file->len;
d986281999-06-30David Hedbor  file->len += misc->_log_cheat_addition;
f7d9811997-09-12Per Hedbor  conf->log(file, this_object());
d7b0871997-08-31Per Hedbor  } }
903e571998-03-25Per Hedbor  end(0,1);
e5bad21998-02-10Per Hedbor  return;
d7b0871997-08-31Per Hedbor }
fa44bd1998-03-28David Hedbor #ifdef FD_DEBUG
4949d71998-03-28David Hedbor void timer(int start) {
aa92c11998-08-20Henrik Grubbström (Grubba)  if(pipe) { // FIXME: Disconnect if no data has been sent for a long while // (30min?)
42ba931998-03-28David Hedbor  MARK_FD(sprintf("HTTP_piping_%d_%d_%d_%d_(%s)",
2c499e1998-03-28David Hedbor  pipe->sent, stringp(pipe->current_input) ?
b529001998-03-28David Hedbor  strlen(pipe->current_input) : -1,
9feab41998-03-28David Hedbor  pipe->last_called,
42ba931998-03-28David Hedbor  _time(1) - start, 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  string handle_error_file_request(array err, int eid) { // return "file request for "+variables->file+"; line="+variables->line; string data = Stdio.read_bytes(variables->file); array(string) bt = (describe_backtrace(err)/"\n") - ({""}); string down; if((int)variables->off-1 >= 1) down = link_to( bt[(int)variables->off-1],eid, (int)variables->off-1); else down = "<a>"; if(data) { int off = 49; array (string) lines = data/"\n"; int start = (int)variables->line-50; if(start < 0) { off += start; start = 0; } int end = (int)variables->line+50; lines=highlight_pike("foo", ([ "nopre":1 ]), lines[start..end]*"\n")/"\n"; // foreach(bt, string b) // { // int line; // string file, fun; // sscanf(what, "%s(%*s in line %d in %s", fun, line, file); // if(file && fun && line) sscanf(file, "%s (", file); // if((file == variables->file) && // (fun == variables->fun) && // (line == variables->line)) // } if(sizeof(lines)>off) lines[off]=("<font size=+2><b>"+down+lines[off]+"</a></b></font></a>"); lines[max(off-20,0)] = "<a name=here>"+lines[max(off-20,0)]+"</a>"; data = lines*"\n"; } return format_backtrace(bt,eid)+"<hr noshade><pre>"+data+"</pre>"; }
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 = ""; void create(mapping _file, mapping heads, array _ranges) { file = _file->file; len = _file->len; foreach(indices(heads), string h) { if(lower_case(h) == "content-type") { type = heads[h]; m_delete(heads, h); } } heads["Content-Type"] = "multipart/byteranges; boundary=" BOUND; ranges = _ranges; int clen; foreach(ranges, array range) { int rlen = 1+ range[1] - range[0]; string sep = sprintf("\r\n--%s\r\nContent-Type: %O\r\n" "Content-Range: bytes %d-%d/%d\r\n\r\n", BOUND, type, @range, len); 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); foreach(ranges, array range) { rlen = range_info[0][0] - current_pos; if(separator != 1) { // New range, write new separator.
4ee9601999-07-02David Hedbor  // write("Initiating new range %d -> %d.\n", @range);
d986281999-06-30David Hedbor  out += range_info[0][1]; num_bytes -= strlen(range_info[0][1]); file->seek(range[0]); separator = 1; } if(num_bytes > 0) { if(rlen <= num_bytes) // Entire range fits. { out += file->read(rlen); num_bytes -= rlen; current_pos = separator = 0; ranges = ranges[1..]; // One range done. range_info = range_info[1..]; } else { out += file->read(num_bytes); current_pos += num_bytes; num_bytes = 0; } } if(strlen(out) > total) { // Oops. too much data. Send amount asked for and save // the rest. stored_data = out[total..]; return out[..total-1]; } } if(separator != 2) { // End boundary. Only write once. separator = 2; out += "\r\n--" BOUND "--\r\n"; } if(strlen(out) > total) { // Oops. too much data again. Write and store. Write and store. stored_data = out[total..]; return out[..total-1]; } stored_data = ""; // Very important. Ia. return out ; // We are finally done. } mixed `->(string what) { switch(what) { case "read": return read; case "set_nonblocking": return 0; default: return file[what]; } } } // Parse the range header itno multiple ranges. array parse_range_header(int len) { array ranges = ({}); foreach(misc->range / ",", string range) { int r1, r2; if(range[0] == '-' ) { // End of file request r1 = (len - (int)range[1..]); if(r1 < 0) { // Entire file requested here. r1 = 0; } ranges += ({ ({ len - (int)range[1..], len-1 }) }); } else if(range[-1] == '-') { // Rest of file request r1 = (int)range; if(r1 >= len) // Range beginning is after EOF. continue; 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 }) }); } else // A syntatically incorrect range should make the server // ignore the header. Really. return 0; } else // Invalid syntax again... return 0; } return ranges; }
45b22b1998-10-26Henrik Grubbström (Grubba) // Tell the client that it can start sending some more data void ready_to_receive() { if (clientprot == "HTTP/1.1") { my_fd->write("HTTP/1.1 100 Continue\r\n"); } }
aa92c11998-08-20Henrik Grubbström (Grubba) 
d943c81998-05-18Henrik Grubbström (Grubba) // Send the result. void send_result(mapping|void result)
b1fca01996-11-12Per Hedbor {
d943c81998-05-18Henrik Grubbström (Grubba)  array err;
5bc1991997-01-29Per Hedbor  int tmp;
b1fca01996-11-12Per Hedbor  mapping heads;
5bc1991997-01-29Per Hedbor  string head_string;
d943c81998-05-18Henrik Grubbström (Grubba)  if (result) { file = result;
14179b1997-01-29Per Hedbor  }
82f5191997-03-02Per Hedbor 
9d8ea11999-04-16Henrik Grubbström (Grubba)  DPERROR(sprintf("HTTP: send_result(%O)", file));
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]);
d943c81998-05-18Henrik Grubbström (Grubba)  else if(err = catch { file=http_low_answer(404,
35c2631999-02-16Per Hedbor  replace(parse_rxml(conf->query("ZNoSuchFile"), this_object()),
d943c81998-05-18Henrik Grubbström (Grubba)  ({"$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  {
903e571998-03-25Per Hedbor  if(do_not_disconnect) { file = 0; pipe = 0; return; }
aa92c11998-08-20Henrik Grubbström (Grubba)  my_fd = 0; file = 0;
14179b1997-01-29Per Hedbor  return;
b1fca01996-11-12Per Hedbor  }
ebb1c51998-02-24Per Hedbor  if(file->type == "raw") file->raw = 1; else if(!file->type) file->type="text/plain";
14179b1997-01-29Per Hedbor  }
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"],
d986281999-06-30David Hedbor  "Accept-Ranges": "bytes",
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); }
d986281999-06-30David Hedbor 
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 
d986281999-06-30David Hedbor  if(misc->range && file->len && objectp(file->file) && !file->data && file->error == 200 && (method == "GET" || method == "HEAD")) // Plain and simple file and a Range header. Let's play. // Also we only bother with 200-requests. Anything else should be // nicely and completely ignored. Also this is only used for GET and // HEAD requests. { // split the range header. If no valid ranges are found, ignore it. // If one is found, send that range. If many are found we need to // use a wrapper and send a multi-part message. array ranges = parse_range_header(file->len); if(ranges) // No incorrect syntax... { if(sizeof(ranges)) // And we have valid ranges as well. { file->error = 206; // 206 Partial Content if(sizeof(ranges) == 1) { heads["Content-Range"] = sprintf("bytes %d-%d/%d", @ranges[0], file->len); file->file->seek(ranges[0][0]); if(ranges[0][1] == (file->len - 1) &&
4ee9601999-07-02David Hedbor  GLOBVAR(RestoreConnLogFull))
d986281999-06-30David Hedbor  // Log continuations (ie REST in FTP), 'range XXX-' // using the entire length of the file, not just the // "sent" part. Ie add the "start" byte location when logging misc->_log_cheat_addition = ranges[0][0]; file->len = ranges[0][1] - ranges[0][0]+1; } else { // Multiple ranges. Multipart reply and stuff needed. // We do this by replacing the file object with a wrapper. // Nice and handy. file->file = MultiRangeWrapper(file, heads, ranges); } } else { // Got the header, but the specified ranges was out of bounds. // Reply with a 416 Requested Range not satisfiable. file->error = 416; heads["Content-Range"] = "*/"+file->len; if(method == "GET") { file->data = "The requested byte range is out-of-bounds. Sorry."; file->len = strlen(file->data); file->file = 0; } } } }
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 
d986281999-06-30David Hedbor  if(file->len > -1) 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 
7a0ee61998-06-27Henrik Grubbström (Grubba) #ifdef REQUEST_DEBUG roxen_perror(sprintf("Sending result for prot:%O, method:%O file:%O\n", prot, method, file)); #endif /* REQUEST_DEBUG */
ebb1c51998-02-24Per Hedbor  if(method == "HEAD") { file->file = 0; file->data=""; }
bd22231998-03-27David Hedbor  MARK_FD("HTTP handled");
ebb1c51998-02-24Per Hedbor 
903e571998-03-25Per Hedbor #ifdef KEEP_ALIVE
ebb1c51998-02-24Per Hedbor  if(!leftovers) leftovers = data||"";
903e571998-03-25Per Hedbor #endif
ebb1c51998-02-24Per Hedbor  if(file->len > 0 && file->len < 2000)
beaca01998-02-20Per Hedbor  {
7a0ee61998-06-27Henrik Grubbström (Grubba)  my_fd->write((head_string || "") +
e268ca1999-05-24Henrik Grubbström (Grubba)  (file->file?file->file->read(file->len): (file->data[..file->len-1])));
beaca01998-02-20Per Hedbor  do_log(); return; }
ebb1c51998-02-24Per Hedbor 
b1fca01996-11-12Per Hedbor  if(head_string) send(head_string);
ebb1c51998-02-24Per Hedbor 
18b3161998-03-25David Hedbor  if(method != "HEAD" && file->error != 304) // No data for these two...
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);
18b3161998-03-25David Hedbor  } else file->len = 1; // Keep those alive, please...
9d8ea11999-04-16Henrik Grubbström (Grubba) 
336ee41999-04-17Henrik Grubbström (Grubba)  start_sender(do_log);
14179b1997-01-29Per Hedbor }
d943c81998-05-18Henrik Grubbström (Grubba)  // Execute the request void handle_request( ) {
9d8ea11999-04-16Henrik Grubbström (Grubba)  DPERROR("HTTP: handle_request()");
d943c81998-05-18Henrik Grubbström (Grubba) #ifdef MAGIC_ERROR if(prestate->old_error) { array err = get_error(variables->error); if(err) { if(prestate->plain) { file = ([ "type":"text/html",
f288181998-05-25Per Hedbor  "data":generate_bugreport( @err ),
d943c81998-05-18Henrik Grubbström (Grubba)  ]);
8a80d41999-05-20Per Hedbor  send_result(); return;
d943c81998-05-18Henrik Grubbström (Grubba)  } else { if(prestate->find_file) { if(!realauth) file = http_auth_required("admin"); else { array auth = (realauth+":")/":";
35c2631999-02-16Per Hedbor  if((auth[0] != roxen.query("ConfigurationUser")) || !crypt(auth[1], roxen.query("ConfigurationPassword")))
d943c81998-05-18Henrik Grubbström (Grubba)  file = http_auth_required("admin"); else file = ([ "type":"text/html",
f288181998-05-25Per Hedbor  "data":handle_error_file_request( err[0], (int)variables->error ),
d943c81998-05-18Henrik Grubbström (Grubba)  ]); }
8a80d41999-05-20Per Hedbor  send_result(); return;
d943c81998-05-18Henrik Grubbström (Grubba)  } } } } #endif /* MAGIC_ERROR */ remove_call_out(do_timeout); MARK_FD("HTTP handling request");
35c2631999-02-16Per Hedbor  array e; if(conf) { if(e= catch(file = conf->handle_request( this_object() ))) internal_error( e ); } else if(!file&&(e=catch(file=roxen.configuration_parse(this_object())))) { if(e==-1) return; internal_error(e);
d943c81998-05-18Henrik Grubbström (Grubba)  } send_result(); }
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;
3235691998-03-26Per Hedbor  MARK_FD("HTTP got data");
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.
3d5e911998-05-21David Hedbor  time = _time(1); // Check is made towards this to make sure the object // is not killed prematurely.
14179b1997-01-29Per Hedbor  if(wanted_data) { if(strlen(s)+have_data < wanted_data) { cache += ({ s }); have_data += strlen(s);
9d8ea11999-04-16Henrik Grubbström (Grubba)  DPERROR("HTTP: We want more data.");
14179b1997-01-29Per Hedbor  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) {
fb5f681998-11-04Peter Bortas  case 0:
d943c81998-05-18Henrik Grubbström (Grubba)  if(this_object()) cache = ({ s }); // More on the way.
9d8ea11999-04-16Henrik Grubbström (Grubba)  DPERROR("HTTP: Request needs more data.");
14179b1997-01-29Per Hedbor  return; case 1:
9d8ea11999-04-16Henrik Grubbström (Grubba)  DPERROR("HTTP: Stupid Client Error");
14179b1997-01-29Per Hedbor  end(prot+" 500 Stupid Client Error\r\nContent-Length: 0\r\n\r\n"); return; // Stupid request. case 2:
9d8ea11999-04-16Henrik Grubbström (Grubba)  DPERROR("HTTP: Done");
14179b1997-01-29Per Hedbor  end(); return; }
ebb1c51998-02-24Per Hedbor 
14179b1997-01-29Per Hedbor  if(conf) {
96682c1999-05-15Henrik Grubbström (Grubba)  // IP-Less support. conf = roxen->find_site_for(this_object());
14179b1997-01-29Per Hedbor  conf->received += strlen(s); conf->requests++; }
2154ca1997-08-31Per Hedbor 
9d8ea11999-04-16Henrik Grubbström (Grubba)  DPERROR("HTTP: Calling roxen.handle().");
2154ca1997-08-31Per Hedbor  my_fd->set_close_callback(0); my_fd->set_read_callback(0);
ebb1c51998-02-24Per Hedbor  processed=1;
35c2631999-02-16Per Hedbor  roxen.handle(this_object()->handle_request);
b1fca01996-11-12Per Hedbor } /* 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->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; c->my_fd = 0; 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->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();
3235691998-03-26Per Hedbor  else if((_time(1) - time) > 4800) end();
b1fca01996-11-12Per Hedbor }
14179b1997-01-29Per Hedbor void create(object f, object c)
b1fca01996-11-12Per Hedbor {
14179b1997-01-29Per Hedbor  if(f) {
65924d1998-07-24David Hedbor  f->set_nonblocking();
14179b1997-01-29Per Hedbor  my_fd = f; conf = c;
3235691998-03-26Per Hedbor  MARK_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);
3d5e911998-05-21David Hedbor  time = _time(1);
14179b1997-01-29Per Hedbor  }
b1fca01996-11-12Per Hedbor }
ceb9271997-05-15David Hedbor 
ebb1c51998-02-24Per Hedbor void chain(object f, object c, string le) { my_fd = f; conf = c; do_not_disconnect=-1;
9211b21998-04-24Per Hedbor  MARK_FD("Kept alive");
4949d71998-03-28David Hedbor  if(strlen(le)) // More to handle already. got_data(0,le);
18c62d1999-05-27Stephen R. van den Berg  else { // If no pipelined data is available, call out... call_out(do_timeout, 150); time = _time(1); }
4949d71998-03-28David Hedbor 
ebb1c51998-02-24Per Hedbor  if(!my_fd) { if(do_not_disconnect == -1) { do_not_disconnect=0; disconnect(); }
903e571998-03-25Per Hedbor  } else { if(do_not_disconnect == -1) do_not_disconnect = 0; if(!processed) { f->set_close_callback(end); f->set_read_callback(got_data); }
ebb1c51998-02-24Per Hedbor  } } // void chain(object fd, object conf, string leftovers) // { // call_out(real_chain,0,fd,conf,leftovers); // }
d986281999-06-30David Hedbor