1998-02-24
1998-02-24 22:23:52 by Per Hedbor <ph@opera.com>
-
ebb1c560a4aca17d030e72450ad1c148731ad641
(297 lines)
(+158/-139)
[
Show
| Annotate
]
Branch: 5.2
Added keep-alive
Rev: server/protocols/http.pike:1.57
1:
// This is a roxen module. (c) Informationsvävarna AB 1996.
- constant cvs_version = "$Id: http.pike,v 1.56 1998/02/20 00:58:15 per Exp $";
+ constant cvs_version = "$Id: http.pike,v 1.57 1998/02/24 22:23:52 per Exp $";
// HTTP protocol module.
#include <config.h>
private inherit "roxenlib";
- int first;
+ // int first;
#if efun(gethrtime)
# define HRTIME() gethrtime()
# define HRSEC(X) ((int)((X)*1000000))
15:
# define SECHR(X) ((float)(X))
#endif
+ #ifdef PROFILE
int req_time = HRTIME();
-
+ #endif
constant decode=roxen->decode;
constant find_supports=roxen->find_supports;
23:
constant handle=roxen->handle;
constant _query=roxen->query;
constant thepipe = roxen->pipe;
- import Simulate;
+ constant _time=predef::time;
-
- function _time=predef::time;
+
private static array(string) cache;
private static int wanted_data, have_data;
object conf;
-
+
#include <roxen.h>
#include <module.h>
48:
int do_not_disconnect;
mapping (string:string) variables = ([ ]);
-
+
mapping (string:mixed) misc = ([ ]);
-
+ mapping (string:string) cookies = ([ ]);
multiset (string) prestate = (< >);
multiset (string) config = (< >);
- multiset (string) supports = (< >);
+ multiset (string) supports;
+ multiset (string) pragma = (< >);
string remoteaddr, host;
- array (string) client = ({ });
- array (string) referer = ({ });
- multiset (string) pragma = (< >);
+ array (string) client;
+ array (string) referer;
- mapping (string:string) cookies = ([ ]);
-
+
mapping file;
object my_fd; /* The client. */
79:
string query;
string not_query;
string extra_extension = ""; // special hack for the language module
- string data;
+ string data, leftovers;
array (int|string) auth;
string rawauth, realauth;
string since;
88:
// state variables. Return 0 if more is expected, 1 if done, and -1
// if fatal error.
- void end(string|void);
+ void end(string|void a,int|void b);
private void setup_pipe()
{
98:
return;
}
if(!pipe) pipe=thepipe();
- #ifdef REQUEST_DEBUG
- perror("REQUEST: Pipe setup.\n");
- #endif
- // pipe->output(my_fd);
+
}
void send(string|object what, int|void len)
109:
if(!what) return;
if(!pipe) setup_pipe();
if(!pipe) return;
- #ifdef REQUEST_DEBUG
- perror("REQUEST: Sending some data\n");
- #endif
+
if(stringp(what)) pipe->write(what);
else pipe->input(what,len);
}
137:
rest_query += "&" + http_decode_string( v );
else
rest_query = http_decode_string( v );
- }
+
rest_query=replace(rest_query, "+", "\000"); /* IDIOTIC STUPID STANDARD */
-
+ }
return f;
}
217:
multiset (string) sup;
array mod_config;
mixed f, line;
- string a, b, linename, contents, real_raw;
+ string a, b, linename, contents;
int config_in_url;
- // roxen->httpobjects[my_id] = "Parsed data...";
- real_raw = s;
- s -= "\r"; // I just hate all thoose CR LF.
+
- if((s[-1] != '\n') || (search(s, "HTTP/") >= 0))
- if(search(s, "\n\n") == -1)
- return 0;
+ // roxen->httpobjects[my_id] = "Parsed data...";
raw = s;
- s = replace(s, "\t", " ");
-
+ if(s[-1] != '\n' || search(s, "\r\n\r\n") == -1)
+ return 0;
#if 0
-
+ /* This version is 8(!) times faster... */
if(sscanf(s,"%s %s %s\n%s", method, f, prot, s) < 4)
{
if(sscanf(s,"%s %s\n", method, f) < 2)
243:
method = "GET";
#else
{
- array arr = s/"\n";
+
// Defaults:
-
+ sscanf(s, "%s\r\n%s", line, s);
+ if(!line) return 0;
prot = clientprot = "HTTP/0.9";
- s="";
+
f="/";
method = "GET";
- if (sizeof(arr[0])) {
- array arr2 = arr[0]/" ";
+ if (sizeof(line))
+ {
+ array arr2 = line/" ";
switch(sizeof(arr2)) {
default:
case 3:
- if ((clientprot = arr2[-1]) != "HTTP/0.9") {
+ if ((clientprot = arr2[-1]) != "HTTP/0.9")
prot = "HTTP/1.0";
- if (sizeof(arr)>1) {
- s = arr[1..]*"\n";
- }
- }
+
f = arr2[1..sizeof(arr2)-2]*" ";
method = arr2[0];
break;
313:
if(strlen(s))
{
- sscanf(real_raw, "%s\r\n\r\n%s", s, data);
+ sscanf(s, "%s\r\n\r\n%s", s, data);
- /* We do _not_ want to parse the 'GET ...\n' line. /Per */
- sscanf(s, "%*s\n%s", s);
+ // s = replace(s, "\n\t", ", ") - "\r";
+ // Handle rfc822 continuation lines and strip \r
- s = replace(s, "\n\t", ", ") - "\r";
- // Handle rfc822 continuation lines and strip \r
-
- foreach(s/"\n" - ({ "" }), line)
+ foreach(s/"\r\n" - ({ "" }), line)
{
linename=contents=0;
sscanf(line, "%s:%s", linename, contents);
335:
switch (linename) {
case "content-length":
misc->len = (int)(contents-" ");
-
+ if(!misc->len) continue;
if(method == "POST")
{
if(!data) data="";
- int l = (int)(contents-" ")-1; /* Length - 1 */
+ int l = misc->len-1; /* Length - 1 */
wanted_data=l;
have_data=strlen(data);
347:
{
return 0;
}
+ leftovers = data[l+1..];
data = data[..l];
switch(lower_case(((misc["content-type"]||"")/";")[0]-" "))
{
418:
break;
case "user-agent":
+ if(!client)
+ {
sscanf(contents, "%s via", contents);
client = contents/" " - ({ "" });
-
+ }
break;
/* Some of M$'s non-standard user-agent info */
427:
case "ua-color": /* Color scheme */
case "ua-os": /* OS-name */
case "ua-cpu": /* CPU-type */
- /* None of the above are interresting or useful */
+ /* None of the above are interresting or useful for us */
/* IGNORED */
break;
439: Inside #if defined(DEBUG)
#ifdef DEBUG
perror("Client extension: "+contents+"\n");
#endif
- linename="extension";
+
case "connection":
contents = lower_case(contents);
506:
break;
case "if-modified-since":
- // if(QUERY(IfModified))
+
since=contents;
break;
- case "negotiate":
- misc["negotiate"]=contents;
- break;
-
+
case "via":
- misc["via"]=contents;
- break;
-
+
case "cache-control":
- misc["cache-control"]=contents;
- break;
-
-
+ case "negotiate":
case "forwarded":
- misc["forwarded"]=contents;
+ misc[linename]=contents;
break;
-
+
#ifdef DEBUG
default:
/* x-* headers are experimental. */
537:
}
}
}
+ if(!client) client = ({ "unknown" });
+ if(!supports)
supports = find_supports(lower_case(client*" "));
-
+ if(!referer) referer = ({ });
if(misc->proxyauth) {
// The Proxy-authorization header should be removed... So there.
mixed tmp1,tmp2;
557:
config = prestate;
else
if(conf
+ && QUERY(set_cookie)
&& !cookies->RoxenUserID && strlen(not_query)
- && not_query[0]=='/' && method!="PUT"
- && QUERY(set_cookie))
+ && not_query[0]=='/' && method!="PUT")
{
if (!(QUERY(set_cookie_only_once) &&
cache_lookup("hosts_for_cookie",remoteaddr))) {
573:
void disconnect()
{
- if(do_not_disconnect)
- {
- #ifdef REQUEST_DEBUG
- perror("REQUEST: Not disconnecting...\n");
- #endif
- return;
- }
- #ifdef REQUEST_DEBUG
- perror("REQUEST: Disconnecting...\n");
- #endif
- if(mappingp(file) && objectp(file->file))
- if (file->file->no_destruct)
- file->file = 0;
- // sslfile, or similar. object will be closed on return.
- else
- destruct(file->file);
-
+ if(do_not_disconnect) return;
+ file = 0;
my_fd = 0;
destruct();
}
- void end(string|void s)
+ void end(string|void s, int|void keepit)
{
- #ifdef REQUEST_DEBUG
- perror("REQUEST: End...\n");
- #endif
+
#ifdef PROFILE
if(conf)
{
611: Inside #if defined(PROFILE)
if(elapsed > p[2]) p[2]=elapsed;
}
#endif
+
+ #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
+
if(objectp(my_fd))
{
-
+ catch {
+ my_fd->set_close_callback(0);
+ my_fd->set_read_callback(0);
my_fd->set_blocking();
if(s) my_fd->write(s);
- if (!my_fd->no_destruct)
- {
+
my_fd->close();
destruct(my_fd);
- }
+ };
my_fd = 0;
}
disconnect();
627:
static void do_timeout(mapping foo)
{
- int elapsed = HRTIME()-req_time;
- if(elapsed > HRSEC(30))
+ int elapsed = _time()-time;
+ if(elapsed > 30)
{
- #ifdef REQUEST_DEBUG
- perror("Timeout. Closing down...\n");
- #endif
+
end("HTTP/1.0 408 Timeout\r\n"
"Content-type: text/plain\r\n"
"Server: Roxen Challenger\r\n"
640:
"Your connection timed out.\n"
"Please try again.\n");
} else {
- // premature call_out...
+ // premature call_out... *¤#!"
call_out(do_timeout, 10);
}
}
705:
if(conf)
{
int len;
- if(pipe && (len = pipe->bytes_sent()))
- file->len = len;
+ if(pipe) file->len = pipe->bytes_sent();
+ // werror("Managed to send "+file->len+" bytes\n");
if(conf)
{
if(file->len > 0) conf->sent+=file->len;
conf->log(file, this_object());
}
}
- end();
+ end("",1);
return;
}
732:
{
// perror("Handle request, got conf.\n");
object oc = conf;
- foreach(conf->first_modules(), funp) {
+ foreach(conf->first_modules(), funp)
+ {
if(file = funp( thiso)) break;
- if(conf != oc) {handle_request();return;}
+ if(conf != oc) {
+ handle_request();
+ return;
}
-
+ }
if(!file) err=catch(file = conf->get_file(thiso));
if(err) internal_error(err);
766:
if((file->file == -1) || file->leave_me)
{
if(do_not_disconnect) return;
- // perror("Leave me...\n");
- // if(!file->stay) { destruct(thiso); }
+
my_fd = 0; file = 0;
return;
}
- if(file->type == "raw")
- file->raw = 1;
- else if(!file->type)
- file->type="text/plain";
+ if(file->type == "raw") file->raw = 1;
+ else if(!file->type) file->type="text/plain";
}
if(!file->raw && prot != "HTTP/0.9")
{
string h;
heads=
- ([
- "MIME-Version":(file["mime-version"] || "1.0"),
+ (["MIME-Version":(file["mime-version"] || "1.0"),
"Content-type":file["type"],
"Server":replace(version(), " ", "·"),
- "Date":http_date(time)
- ]);
+ "Date":http_date(time) ]);
if(file->encoding)
heads["Content-Encoding"] = file->encoding;
817:
if(is_modified(since, fstat[3], fstat[1]))
{
file->error = 304;
- method="HEAD";
+ file->file = 0;
+ file->data="";
+ // method="";
}
}
}
848:
if(conf) conf->hsent+=strlen(head_string||"");
}
- if((method != "HEAD") && (file->len > 0 && file->len < 2000))
+
+ if(method == "HEAD")
{
-
+ file->file = 0;
+ file->data="";
+ }
+
+
+ if(!leftovers) leftovers = data||"";
+
+ if(file->len > 0 && file->len < 2000)
+ {
my_fd->write(head_string + (file->file?file->file->read():file->data));
do_log();
return;
}
-
+
if(head_string) send(head_string);
-
+
+
+
if(method != "HEAD")
{
- if(file->data) send(file->data, file->len);
- if(file->file) send(file->file, file->len);
+ if(file->data && strlen(file->data))
+ send(file->data, file->len);
+ if(file->file)
+ send(file->file, file->len);
}
- pipe->set_done_callback( do_log,0 );
+ pipe->set_done_callback( do_log );
pipe->output(my_fd);
}
/* We got some data on a socket.
* =================================================
*/
-
+ int processed;
void got_data(mixed fooid, string s)
{
-
+ // werror("got_data: '"+s+"'\n");
+
int tmp;
- // perror("Got data.\n");
+
remove_call_out(do_timeout);
call_out(do_timeout, 30); // Close down if we don't get more data
// within 30 seconds. Should be more than enough.
887:
if(cache)
{
- cache += ({ s });
- s = cache*"";
+ s = cache*""+s;
cache = 0;
}
- tmp = parse_got(s);
+ sscanf(s, "%*[\n\r]%s", s);
+ if(strlen(s)) tmp = parse_got(s);
+
switch(-tmp)
{
case 0:
906:
end();
return;
}
+
if(conf)
{
conf->received += strlen(s);
915:
my_fd->set_close_callback(0);
my_fd->set_read_callback(0);
my_fd->set_blocking();
+ processed=1;
#ifdef THREADS
roxen->handle(this_object()->handle_request);
#else
930:
object c,t;
c=object_program(t=this_object())();
- c->first = first;
-
+ // c->first = first;
c->conf = conf;
c->time = time;
c->raw_url = raw_url;
995:
}
}
+ 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);
+ // }