3709192002-03-20Martin Nilsson #pike __REAL_VERSION__
4230532017-02-14Henrik Grubbström (Grubba) //! This class represents a connection from a client to the server. //! //! There are three different read callbacks that can be active, which //! have the following call graphs. @[read_cb] is the default read //! callback, installed by @[attach_fd]. //!
27ac812017-07-25Pontus Östlund //! @code
4230532017-02-14Henrik Grubbström (Grubba) //! | (Incoming data) //! v //! @[read_cb] //! | If complete headers are read //! v //! @[parse_request] //! v //! @[parse_variables] //! | If callback isn't changed to @[read_cb_chunked] or @[read_cb_post] //! v //! @[finalize]
27ac812017-07-25Pontus Östlund //! @endcode
4230532017-02-14Henrik Grubbström (Grubba) //!
27ac812017-07-25Pontus Östlund //! @code
4230532017-02-14Henrik Grubbström (Grubba) //! | (Incoming data) //! v //! @[read_cb_post] //! | If enough data has been received //! v //! @[finalize]
27ac812017-07-25Pontus Östlund //! @endcode
4230532017-02-14Henrik Grubbström (Grubba) //!
27ac812017-07-25Pontus Östlund //! @code
4230532017-02-14Henrik Grubbström (Grubba) //! | (Incoming data) //! v //! @[read_cb_chunked] //! | If all data chunked transfer-encoding needs //! v //! @[finalize]
27ac812017-07-25Pontus Östlund //! @endcode
06a13f2005-12-14Martin Nilsson 
4819b62006-04-19Martin Nilsson  int max_request_size = 0; void set_max_request_size(int size) { max_request_size = size; }
d445cd2001-05-31Mirar (Pontus Hagland) 
9eaf1d2008-06-28Martin Nilsson protected class Port {
6fa30c2004-03-05Henrik Grubbström (Grubba)  Stdio.Port port; int portno; string|int(0..0) interface; function(.Request:void) callback; program request_program=.Request; void create(function(.Request:void) _callback, void|int _portno, void|string _interface); void close();
c071bc2017-11-05Henrik Grubbström (Grubba)  protected void _destruct();
6fa30c2004-03-05Henrik Grubbström (Grubba) }
ff4a952012-11-01Chris Angelico //! The socket that this request came in on.
d6589b2014-05-22Martin Nilsson Stdio.NonblockingStream my_fd;
ff4a952012-11-01Chris Angelico 
2b832c2001-10-10Mirar (Pontus Hagland) Port server_port;
6fa30c2004-03-05Henrik Grubbström (Grubba) .HeaderParser headerparser;
d445cd2001-05-31Mirar (Pontus Hagland)  string buf=""; // content buffer
8fe3f92002-06-15Johan Sundström //! raw unparsed full request (headers and body)
1d42552014-09-03Per Hedbor string raw = "";
8fe3f92002-06-15Johan Sundström  //! raw unparsed body of the request (@[raw] minus request line and headers)
d53def2005-12-02Per Hedbor string body_raw="";
8fe3f92002-06-15Johan Sundström 
52d7b92002-06-17H. William Welliver III //! full request line (@[request_type] + @[full_query] + @[protocol])
d445cd2001-05-31Mirar (Pontus Hagland) string request_raw;
52d7b92002-06-17H. William Welliver III  //! HTTP request method, eg. POST, GET, etc.
d445cd2001-05-31Mirar (Pontus Hagland) string request_type;
52d7b92002-06-17H. William Welliver III  //! full resource requested, including attached GET query
d445cd2001-05-31Mirar (Pontus Hagland) string full_query;
52d7b92002-06-17H. William Welliver III  //! resource requested minus any attached query
d445cd2001-05-31Mirar (Pontus Hagland) string not_query;
52d7b92002-06-17H. William Welliver III  //! query portion of requested resource, starting after the first "?"
d445cd2001-05-31Mirar (Pontus Hagland) string query;
52d7b92002-06-17H. William Welliver III  //! request protocol and version, eg. HTTP/1.0
d445cd2001-05-31Mirar (Pontus Hagland) string protocol;
17b06f2002-02-04Per Hedbor int started = time();
52d7b92002-06-17H. William Welliver III //! all headers included as part of the HTTP request, ie content-type.
28d50b2001-06-06Mirar (Pontus Hagland) mapping(string:string|array(string)) request_headers=([]);
d445cd2001-05-31Mirar (Pontus Hagland) 
52d7b92002-06-17H. William Welliver III //! all variables included as part of a GET or POST request.
d445cd2001-05-31Mirar (Pontus Hagland) mapping(string:string|array(string)) variables=([]);
52d7b92002-06-17H. William Welliver III  //! cookies set by client
28d50b2001-06-06Mirar (Pontus Hagland) mapping(string:string) cookies=([]);
52d7b92002-06-17H. William Welliver III //! external use only mapping misc=([]);
d445cd2001-05-31Mirar (Pontus Hagland) 
06728a2004-01-24Mirar (Pontus Hagland) //! send timeout (no activity for this period with data in send buffer) //! in seconds, default is 180 int send_timeout_delay=180;
e2f44e2004-01-24Mirar (Pontus Hagland) //! connection timeout, delay until connection is closed while //! waiting for the correct headers: int connection_timeout_delay=180;
d445cd2001-05-31Mirar (Pontus Hagland) function(this_program:void) request_callback;
cc1f1e2013-04-16Markus Ottensmann function(this_program,array:void) error_callback;
d445cd2001-05-31Mirar (Pontus Hagland) 
1d42552014-09-03Per Hedbor System.Timer startt = System.Timer();
d6589b2014-05-22Martin Nilsson void attach_fd(Stdio.NonblockingStream _fd, Port server,
e2f44e2004-01-24Mirar (Pontus Hagland)  function(this_program:void) _request_callback,
cc1f1e2013-04-16Markus Ottensmann  void|string already_data, void|function(this_program,array:void) _error_callback)
d445cd2001-05-31Mirar (Pontus Hagland) { my_fd=_fd; server_port=server;
6fa30c2004-03-05Henrik Grubbström (Grubba)  headerparser = .HeaderParser();
d445cd2001-05-31Mirar (Pontus Hagland)  request_callback=_request_callback;
cc1f1e2013-04-16Markus Ottensmann  error_callback = _error_callback;
d445cd2001-05-31Mirar (Pontus Hagland)  my_fd->set_nonblocking(read_cb,0,close_cb);
e2f44e2004-01-24Mirar (Pontus Hagland)  call_out(connection_timeout,connection_timeout_delay);
1d42552014-09-03Per Hedbor  if (already_data && strlen(already_data))
e2f44e2004-01-24Mirar (Pontus Hagland)  read_cb(0,already_data);
d445cd2001-05-31Mirar (Pontus Hagland) }
189fd62005-09-09Per Hedbor  // Some (wap-gateways, specifically) servers send multiple // content-length, as an example.. constant singular_headers = ({ "content-length", "content-type", "connection",
64e1f32005-11-28Per Hedbor  "transfer-encoding",
189fd62005-09-09Per Hedbor  "if-modified-since", "date",
442f292006-05-08Per Hedbor  "pragma",
189fd62005-09-09Per Hedbor  "range", });
64e1f32005-11-28Per Hedbor // We use these as if we only got one, regardless of the number of // headers received. constant singular_use_headers = ({ "cookie", "cookie2",
1d42552014-09-03Per Hedbor }); private int sent; private OutputBuffer send_buf = OutputBuffer(); private Stdio.File send_fd=0; private int send_stop; private int keep_alive=0;
64e1f32005-11-28Per Hedbor 
9eaf1d2008-06-28Martin Nilsson protected void flatten_headers()
06a13f2005-12-14Martin Nilsson { foreach( singular_headers, string x ) if( arrayp(request_headers[x]) ) request_headers[x] = request_headers[x][-1]; foreach( singular_use_headers, string x ) if( arrayp(request_headers[x]) ) request_headers[x] = request_headers[x]*";"; }
7c7de12018-02-18Martin Nilsson //! Called when the client is attempting opportunistic TLS on this //! HTTP port. Overload to handle, i.e. send the data to a TLS //! port. By default the connection is simply closed. void opportunistic_tls(string s) { close_cb(); }
06a13f2005-12-14Martin Nilsson // Appends data to raw and feeds the header parse with data. Once the // header parser has enough data parse_request() and parse_variables() // are called. If parse_variables() deems the request to be finished // finalize() is called. If not parse_variables() has replaced the // read callback.
9eaf1d2008-06-28Martin Nilsson protected void read_cb(mixed dummy,string s)
d445cd2001-05-31Mirar (Pontus Hagland) {
1d42552014-09-03Per Hedbor  if( !sizeof( raw ) ) {
8246ee2018-02-18Martin Nilsson  // Opportunistic TLS. if( has_prefix(s, "\x16\x03\x01") ) {
7c7de12018-02-18Martin Nilsson  opportunistic_tls(s);
8246ee2018-02-18Martin Nilsson  return; }
1d42552014-09-03Per Hedbor  sscanf(s,"%*[ \t\n\r]%s", s ); if( !strlen( s ) ) return; }
f4263f2002-06-14H. William Welliver III  raw+=s;
e2f44e2004-01-24Mirar (Pontus Hagland)  remove_call_out(connection_timeout);
d445cd2001-05-31Mirar (Pontus Hagland)  array v=headerparser->feed(s); if (v) { destruct(headerparser); headerparser=0; buf=v[0];
28d50b2001-06-06Mirar (Pontus Hagland)  request_headers=v[2];
189fd62005-09-09Per Hedbor 
d445cd2001-05-31Mirar (Pontus Hagland)  request_raw=v[1]; parse_request();
6a81612016-11-05Arne Goedeke  if (parse_variables())
1d42552014-09-03Per Hedbor  finalize();
d445cd2001-05-31Mirar (Pontus Hagland)  }
e2f44e2004-01-24Mirar (Pontus Hagland)  else call_out(connection_timeout,connection_timeout_delay); }
9eaf1d2008-06-28Martin Nilsson protected void connection_timeout()
e2f44e2004-01-24Mirar (Pontus Hagland) { finish(0);
d445cd2001-05-31Mirar (Pontus Hagland) }
4819b62006-04-19Martin Nilsson // Parses the request and populates request_type, protocol, // full_query, query and not_query.
9eaf1d2008-06-28Martin Nilsson protected void parse_request()
d445cd2001-05-31Mirar (Pontus Hagland) { array v=request_raw/" "; switch (sizeof(v)) { case 0: request_type="GET"; protocol="HTTP/0.9"; full_query=""; break; case 1: request_type="GET"; protocol="HTTP/0.9"; full_query=v[0]; break; default: if (v[-1][..3]=="HTTP") { request_type=v[0]; protocol=v[-1];
949e682006-01-02Martin Nilsson  if(!(< "HTTP/1.0", "HTTP/1.1" >)[protocol]) { int maj, min; if(sscanf(protocol, "HTTP/%d.%d", maj, min)==2) protocol = sprintf("HTTP/%d.%d", maj, min); }
7a2c2e2016-07-27Martin Nilsson  // FIXME: This is explicitly against RFC7230. Instead a // 400 Bad Request response should be given, or if we want // to fix the request-target, a 301 Moved Permanently to // the fixed location.
8a531a2006-11-04Martin Nilsson  full_query=v[1..<1]*" ";
d445cd2001-05-31Mirar (Pontus Hagland)  break; }
06a13f2005-12-14Martin Nilsson  // Fallthrough
d445cd2001-05-31Mirar (Pontus Hagland)  case 2: request_type=v[0]; protocol="HTTP/0.9"; full_query=v[1..]*" "; break; }
28d50b2001-06-06Mirar (Pontus Hagland) 
06a13f2005-12-14Martin Nilsson  query = ""; not_query = full_query; sscanf(full_query, "%s?%s", not_query, query);
d445cd2001-05-31Mirar (Pontus Hagland) }
9f244a2005-11-28Per Hedbor enum ChunkedState { READ_SIZE = 1,
64e1f32005-11-28Per Hedbor  READ_CHUNK,
9f244a2005-11-28Per Hedbor  READ_POSTNL,
64e1f32005-11-28Per Hedbor  READ_TRAILER, FINISHED, };
9f244a2005-11-28Per Hedbor private ChunkedState chunked_state = READ_SIZE;
64e1f32005-11-28Per Hedbor private int chunk_size; private string current_chunk = ""; private string actual_data = ""; private string trailers = "";
06a13f2005-12-14Martin Nilsson // Appends data to raw and buf. Parses the data with the clunky- // chunky-algorithm and, when all data has been received, updates // body_raw and request_headers and calls finalize.
64e1f32005-11-28Per Hedbor private void read_cb_chunked( mixed dummy, string data ) {
a1e4562005-11-29Per Hedbor  raw += data;
64e1f32005-11-28Per Hedbor  buf += data;
9f244a2005-11-28Per Hedbor  remove_call_out(connection_timeout); while( chunked_state == FINISHED || strlen( buf ) )
64e1f32005-11-28Per Hedbor  { switch( chunked_state ) { case READ_SIZE: if( !has_value( buf, "\r\n" ) ) return; // SIZE[ extension]*\r\n sscanf( buf, "%x%*[^\r\n]\r\n%s", chunk_size, buf); if( chunk_size == 0 ) chunked_state = READ_TRAILER; else chunked_state = READ_CHUNK;
9f244a2005-11-28Per Hedbor  break;
64e1f32005-11-28Per Hedbor  case READ_CHUNK: int l = min( strlen(buf), chunk_size ); chunk_size -= l; actual_data += buf[..l-1]; buf = buf[l..]; if( !chunk_size )
9f244a2005-11-28Per Hedbor  chunked_state = READ_POSTNL; break; case READ_POSTNL: if( strlen( buf ) < 2 ) return; if( has_prefix( buf, "\r\n" ) ) buf = buf[2..]; chunked_state = READ_SIZE; break;
64e1f32005-11-28Per Hedbor  case READ_TRAILER: trailers += buf; buf = ""; if( has_value( trailers, "\r\n\r\n" ) || has_prefix( trailers, "\r\n" ) ) { chunked_state = FINISHED; if( !has_prefix( trailers, "\r\n" ) ) sscanf( trailers, "%s\r\n\r\n%s", trailers, buf ); else {
9f244a2005-11-28Per Hedbor  buf = buf[2..];
64e1f32005-11-28Per Hedbor  trailers = ""; } } break; case FINISHED: foreach( trailers/"\r\n"-({""}), string header ) { string hk, hv; if( sscanf( header, "%s:%s", hk, hv ) == 2 ) { hk = String.trim_whites(lower_case(hk));
a6628a2018-01-13Martin Nilsson  hv = String.trim_whites(hv); // RFC 7230 4.1.2: Ignore framing, routing, modifiers, // authentication and response control headers. if( (< "transfer-encoding", "content-length", "host", "cache-control", "expect", "max-forwards", "pragma", "range", "te", "age", "expires", "date", "location", "retry-after", "vary", "warning", "authentication", "proxy-authenticate", "proxy-authorization", "www-authenticate" >)[ hk ] ) continue;
64e1f32005-11-28Per Hedbor  if( request_headers[hk] ) { if( !arrayp( request_headers[hk] ) ) request_headers[hk] = ({request_headers[hk]}); request_headers[hk]+=({hv}); } else
ece2722018-01-13Martin Nilsson  request_headers[hk] = hv;
64e1f32005-11-28Per Hedbor  } } // And FINALLY we are done..
a1e4562005-11-29Per Hedbor  body_raw = actual_data;
64e1f32005-11-28Per Hedbor  request_headers["content-length"] = ""+strlen(actual_data); finalize(); return; } }
9f244a2005-11-28Per Hedbor  call_out(connection_timeout,connection_timeout_delay);
64e1f32005-11-28Per Hedbor }
6a81612016-11-05Arne Goedeke protected int parse_variables()
d445cd2001-05-31Mirar (Pontus Hagland) {
a1e4562005-11-29Per Hedbor  if (query!="") .http_decode_urlencoded_query(query,variables);
d445cd2001-05-31Mirar (Pontus Hagland) 
06a13f2005-12-14Martin Nilsson  flatten_headers();
64e1f32005-11-28Per Hedbor 
1d42552014-09-03Per Hedbor  if ( request_headers->expect )
442f292006-05-08Per Hedbor  { if ( lower_case(request_headers->expect) == "100-continue" ) my_fd->write("HTTP/1.1 100 Continue\r\n\r\n"); }
1d42552014-09-03Per Hedbor  if( request_headers["transfer-encoding"] &&
a1e4562005-11-29Per Hedbor  has_value(lower_case(request_headers["transfer-encoding"]),"chunked")) { my_fd->set_read_callback(read_cb_chunked); read_cb_chunked(0,""); return 0; }
1d42552014-09-03Per Hedbor 
a1e4562005-11-29Per Hedbor  int l = (int)request_headers["content-length"]; if (l<=sizeof(buf)) { int zap = strlen(buf) - l;
8a531a2006-11-04Martin Nilsson  raw = raw[..<zap-1];
a1e4562005-11-29Per Hedbor  body_raw = buf[..l-1]; buf = buf[l..]; return 1; } my_fd->set_read_callback(read_cb_post); return 0; // delay
d445cd2001-05-31Mirar (Pontus Hagland) }
10d3e82014-09-23Martin Nilsson protected void update_mime_var(string name, string new) {
9f07292014-09-23Martin Nilsson  string|array val = variables[name];
10d3e82014-09-23Martin Nilsson  if( !val ) { variables[name] = new; return; } if( !arrayp(val) ) variables[name] = ({ val }); variables += ({ new }); }
9eaf1d2008-06-28Martin Nilsson protected void parse_post()
d445cd2001-05-31Mirar (Pontus Hagland) {
3524712015-05-26Martin Nilsson  if ( request_headers["content-type"] &&
a1e4562005-11-29Per Hedbor  has_prefix(request_headers["content-type"], "multipart/form-data") ) {
645afc2012-02-27Martin Stjernholm  MIME.Message messg = MIME.Message(body_raw, request_headers, 0, 1);
6c491b2006-03-04Martin Nilsson  if(!messg->body_parts) return;
a1e4562005-11-29Per Hedbor 
10d3e82014-09-23Martin Nilsson  foreach(messg->body_parts, object part) { string name = part->disp_params->name; if(!name) continue;
a1e4562005-11-29Per Hedbor  if(part->disp_params->filename) {
10d3e82014-09-23Martin Nilsson  update_mime_var(name, part->getdata()); update_mime_var(name+".filename", part->disp_params->filename); update_mime_var(name+".mimetype", part->disp_params->filename);
31ebdf2013-05-21Markus Ottensmann  } else
10d3e82014-09-23Martin Nilsson  variables[name] = part->getdata();
a1e4562005-11-29Per Hedbor  } }
8c92d22006-01-19Martin Nilsson  else if( request_headers["content-type"] && ( has_value(request_headers["content-type"], "url-encoded") || has_value(request_headers["content-type"], "urlencoded") )) {
a1e4562005-11-29Per Hedbor  .http_decode_urlencoded_query(body_raw,variables);
8c92d22006-01-19Martin Nilsson  }
64e1f32005-11-28Per Hedbor }
9eaf1d2008-06-28Martin Nilsson protected void finalize()
64e1f32005-11-28Per Hedbor { my_fd->set_blocking();
06a13f2005-12-14Martin Nilsson  flatten_headers();
cc1f1e2013-04-16Markus Ottensmann  if (array err = catch {parse_post();}) { if (error_callback) error_callback(this, err);
05a67a2013-04-19Henrik Grubbström (Grubba)  else throw(err);
cc1f1e2013-04-16Markus Ottensmann  } else { if (request_headers->cookie) foreach (request_headers->cookie/";";;string cookie) if (sscanf(String.trim_whites(cookie),"%s=%s",string a,string b)==2) cookies[a]=b;
6a81612016-11-05Arne Goedeke  request_callback(this);
cc1f1e2013-04-16Markus Ottensmann  }
d445cd2001-05-31Mirar (Pontus Hagland) }
06a13f2005-12-14Martin Nilsson // Adds incoming data to raw and buf. Once content-length or
4819b62006-04-19Martin Nilsson // max_request_size data has been received, finalize is called.
9eaf1d2008-06-28Martin Nilsson protected void read_cb_post(mixed dummy,string s)
d445cd2001-05-31Mirar (Pontus Hagland) {
a1e4562005-11-29Per Hedbor  raw += s;
d43eb52005-12-02Per Hedbor  buf += s;
9f244a2005-11-28Per Hedbor  remove_call_out(connection_timeout);
a1e4562005-11-29Per Hedbor  int l = (int)request_headers["content-length"];
4819b62006-04-19Martin Nilsson  if (sizeof(buf)>=l || ( max_request_size && sizeof(buf)>max_request_size ))
a1e4562005-11-29Per Hedbor  {
d43eb52005-12-02Per Hedbor  body_raw=buf[..l-1]; buf = buf[l..]; raw = raw[..strlen(raw)-strlen(buf)];
64e1f32005-11-28Per Hedbor  finalize();
a1e4562005-11-29Per Hedbor  }
9f244a2005-11-28Per Hedbor  else call_out(connection_timeout,connection_timeout_delay);
d445cd2001-05-31Mirar (Pontus Hagland) }
9eaf1d2008-06-28Martin Nilsson protected void close_cb()
d445cd2001-05-31Mirar (Pontus Hagland) {
5514232001-05-31Mirar (Pontus Hagland) // closed by peer before request read
99b3392004-01-24Mirar (Pontus Hagland)  if (my_fd) { my_fd->close(); destruct(my_fd); my_fd=0; }
d445cd2001-05-31Mirar (Pontus Hagland) }
bdde722012-09-01Martin Nilsson protected string _sprintf(int t)
d445cd2001-05-31Mirar (Pontus Hagland) {
9242222003-10-28Martin Stjernholm  return t=='O' && sprintf("%O(%O %O)",this_program,request_type,full_query);
d445cd2001-05-31Mirar (Pontus Hagland) } // ----------------------------------------------------------------
17b06f2002-02-04Per Hedbor function log_cb;
1d42552014-09-03Per Hedbor 
d445cd2001-05-31Mirar (Pontus Hagland) string make_response_header(mapping m) {
cb65012014-10-01Martin Nilsson  return (string)low_make_response_header(m,Stdio.Buffer());
1d42552014-09-03Per Hedbor }
cb65012014-10-01Martin Nilsson Stdio.Buffer low_make_response_header(mapping m, Stdio.Buffer res)
1d42552014-09-03Per Hedbor { void radd( mixed ... args ) {
55708c2015-08-28Michał Gawron  res->add(@(array(string))args,"\r\n");
1d42552014-09-03Per Hedbor  };
38c8e72004-01-24Mirar (Pontus Hagland)  if (protocol!="HTTP/1.0")
1d42552014-09-03Per Hedbor  {
38c8e72004-01-24Mirar (Pontus Hagland)  if (protocol=="HTTP/1.1") { // check for fire and forget here and go back to 1.0 then } else protocol="HTTP/1.0";
1d42552014-09-03Per Hedbor  } if (!m->file && !m->data) m->data=""; else if (!m->stat && m->file) m->stat=m->file->stat(); if (undefinedp(m->size)) { if (m->data) m->size=sizeof(m->data); else if (m->stat) { m->size=m->stat->size; if( m->file ) m->size -= m->file->tell(); } else m->size=-1; } if (m->size!=-1) { if (undefinedp(m->start) && m->error==206) { if (m->stop==-1) m->stop=m->size-1; if (m->start>=m->size || m->stop>=m->size || m->stop<m->start || m->size<0) res->error = 416; } }
38c8e72004-01-24Mirar (Pontus Hagland) 
d445cd2001-05-31Mirar (Pontus Hagland)  switch (m->error) { case 0:
1d42552014-09-03Per Hedbor  case 200:
65340d2014-08-15Martin Nilsson  if (undefinedp(m->start))
1d42552014-09-03Per Hedbor  radd(protocol," 200 OK"); // HTTP/1.1 when supported
99b3392004-01-24Mirar (Pontus Hagland)  else {
1d42552014-09-03Per Hedbor  radd(protocol," 206 Partial content");
99b3392004-01-24Mirar (Pontus Hagland)  m->error=206; }
d445cd2001-05-31Mirar (Pontus Hagland)  break; default:
da11cf2011-11-05Bill Welliver  if(Protocols.HTTP.response_codes[(int)m->error])
725a882015-08-31Michał Gawron  radd(protocol," ", Protocols.HTTP.response_codes[(int)m->error]);
da11cf2011-11-05Bill Welliver  else
725a882015-08-31Michał Gawron  radd(protocol," ",m->error," ERROR");
1d42552014-09-03Per Hedbor  break;
d445cd2001-05-31Mirar (Pontus Hagland)  }
6bd2732018-04-09Martin Nilsson  array extra = ({}); if (m->extra_heads) { foreach (m->extra_heads;string name;array|string arr) { extra += ({ lower_case(name) }); if( name=="connection" && has_value(arr, "keep-alive") ) keep_alive=1; foreach (Array.arrayify(arr);;string value) radd(name, ": ", value); } }
d445cd2001-05-31Mirar (Pontus Hagland) 
6bd2732018-04-09Martin Nilsson  if( !has_value(extra, "content-type") ) { if (!m->type) m->type = .filename_to_type(not_query); radd("Content-Type: ", m->type); } if( m->error == 206 && !has_value(extra, "content-range") )
6587252016-06-16Arne Goedeke  radd("Content-Range: bytes ", m->start,"-",m->stop,"/", has_index(m, "instance_size") ? m->instance_size : "*");
d445cd2001-05-31Mirar (Pontus Hagland) 
6bd2732018-04-09Martin Nilsson  if( m->size >= 0 && !has_value(extra, "content-length") )
1d42552014-09-03Per Hedbor  radd("Content-Length: ",(string)m->size);
d445cd2001-05-31Mirar (Pontus Hagland) 
6bd2732018-04-09Martin Nilsson  if( !has_value(extra, "server") ) radd("Server: ", m->server || .http_serverid);
1d42552014-09-03Per Hedbor  string http_now = .http_date(time(1));
6bd2732018-04-09Martin Nilsson  if( !has_value(extra, "date") ) { radd("Date: ",http_now); }
553ab72001-06-06Mirar (Pontus Hagland) 
6bd2732018-04-09Martin Nilsson  if( !has_value(extra, "last-modified") ) { if (m->modified) radd("Last-Modified: ", .http_date(m->modified)); else if (m->stat) radd("Last-Modified: ", .http_date(m->stat->mtime)); else radd("Last-Modified: ", http_now); }
2f2a3b2006-05-08Per Hedbor 
6bd2732018-04-09Martin Nilsson  if( !has_value(extra, "connection") )
2f2a3b2006-05-08Per Hedbor  {
6bd2732018-04-09Martin Nilsson  string cc = lower_case(request_headers["connection"]||""); if( (protocol=="HTTP/1.1" && !has_value(cc,"close")) || cc=="keep-alive" ) {
1d42552014-09-03Per Hedbor  radd("Connection: keep-alive");
2f2a3b2006-05-08Per Hedbor  keep_alive=1;
6bd2732018-04-09Martin Nilsson  } else {
1d42552014-09-03Per Hedbor  radd("Connection: close");
6bd2732018-04-09Martin Nilsson  }
1d42552014-09-03Per Hedbor  }
38c8e72004-01-24Mirar (Pontus Hagland) 
1d42552014-09-03Per Hedbor  res->add("\r\n"); return res;
d445cd2001-05-31Mirar (Pontus Hagland) }
ff4a952012-11-01Chris Angelico //! Return the IP address that originated the request, or 0 if //! the IP address could not be determined. In the event of an //! error, @[my_fd]@tt{->errno()@} will be set. string get_ip() {
3ac53f2015-07-07Arne Goedeke  if (!my_fd) return 0; string addr = my_fd->query_address();
1d42552014-09-03Per Hedbor  if (!addr) return 0; sscanf(addr,"%s ",addr); return addr;
ff4a952012-11-01Chris Angelico }
52d7b92002-06-17H. William Welliver III //! return a properly formatted response to the HTTP client
3524712015-05-26Martin Nilsson //! @param m
52d7b92002-06-17H. William Welliver III //! Contains elements for generating a response to the client. //! @mapping m //! @member string "data" //! Data to be returned to the client. //! @member object "file" //! File object, the contents of which will be returned to the client. //! @member int "error" //! HTTP error code
6587252016-06-16Arne Goedeke //! @member int "size"
3524712015-05-26Martin Nilsson //! length of content returned. If @i{file@} is provided, @i{size@}
52d7b92002-06-17H. William Welliver III //! bytes will be returned to client. //! @member string "modified" //! contains optional modification date. //! @member string "type" //! contains optional content-type //! @member mapping "extra_heads"
3524712015-05-26Martin Nilsson //! contains a mapping of additional headers to be
52d7b92002-06-17H. William Welliver III //! returned to client.
3524712015-05-26Martin Nilsson //! @member string "server"
52d7b92002-06-17H. William Welliver III //! contains the server identification header. //! @endmapping
17b06f2002-02-04Per Hedbor void response_and_finish(mapping m, function|void _log_cb)
d445cd2001-05-31Mirar (Pontus Hagland) {
6587252016-06-16Arne Goedeke  string tmp;
bdde722012-09-01Martin Nilsson  m += ([ ]);
17b06f2002-02-04Per Hedbor  log_cb = _log_cb;
d445cd2001-05-31Mirar (Pontus Hagland) 
2e5d0d2014-09-08Per Hedbor  if( !my_fd ) return;
6587252016-06-16Arne Goedeke  if ((tmp = request_headers->range) && !m->start && undefinedp(m->error))
99b3392004-01-24Mirar (Pontus Hagland)  {
6587252016-06-16Arne Goedeke  /* * The instance-size is the full size of the resource, while the content-length * of a range response is the length of the actual data returned. */ if (!has_index(m, "instance_size")) { if (m->file) { if (!m->stat) m->stat = m->file->stat(); if (m->stat) m->instance_size = m->stat->size; else if (has_index(m, "size")) m->instance_size = m->size; } else if (m->data) { m->instance_size = sizeof(m->data); } else if (has_index(m, "size")) m->instance_size = m->size; }
99b3392004-01-24Mirar (Pontus Hagland)  int a,b;
6587252016-06-16Arne Goedeke  if (sscanf(tmp,"bytes%*[ =]%d-%d",a,b)==3) { m->start=a; m->stop=b; } else if (sscanf(tmp,"bytes%*[ =]-%d",b)==2)
8e7d332010-03-17Martin Nilsson  {
6587252016-06-16Arne Goedeke  if( !has_index(m, "instance_size") )
1d42552014-09-03Per Hedbor  {
8e7d332010-03-17Martin Nilsson  m_delete(m,"file"); m->data=""; m->error=416;
1d42552014-09-03Per Hedbor  } else {
6587252016-06-16Arne Goedeke  m->start=max(0, m->instance_size-b);
1d42552014-09-03Per Hedbor  m->stop=-1; }
8e7d332010-03-17Martin Nilsson  }
6587252016-06-16Arne Goedeke  else if (sscanf(tmp,"bytes%*[ =]%d-",a)==2) { m->start=a; m->stop=-1; } else if (has_value(tmp, ","))
8e7d332010-03-17Martin Nilsson  {
1d42552014-09-03Per Hedbor  // Multiple ranges m_delete(m,"file"); m->data=""; m->error=416;
8e7d332010-03-17Martin Nilsson  }
6587252016-06-16Arne Goedeke  if (has_index(m, "instance_size")) { /* start > instance_size is an invalid request */ if (m->start && m->start >= m->instance_size) { m_delete(m,"file"); m->data=""; m->error=416; m_delete(m,"start"); m_delete(m,"stop"); } else if (m->stop && m->stop >= m->instance_size) { m->stop = m->instance_size - 1; } }
99b3392004-01-24Mirar (Pontus Hagland)  } if (request_headers["if-modified-since"]) {
6fa30c2004-03-05Henrik Grubbström (Grubba)  int t = .http_decode_date(request_headers["if-modified-since"]);
99b3392004-01-24Mirar (Pontus Hagland)  if (t) { if (!m->stat && m->file) m->stat=m->file->stat();
1d05472004-01-24Mirar (Pontus Hagland)  if (m->stat && m->stat->mtime<=t)
99b3392004-01-24Mirar (Pontus Hagland)  { m_delete(m,"file"); m->data=""; m->error=304; } } }
4ef8d02010-12-01Per Hedbor  if (request_headers["if-none-match"] && m->extra_heads ) {
1d42552014-09-03Per Hedbor  string et; if((et = m->extra_heads->ETag) || (et =m->extra_heads->etag)) { if( string key = request_headers["if-none-match"] ) { if (key == et) { m_delete(m,"file"); m->data=""; m->error=304; } } }
4ef8d02010-12-01Per Hedbor  }
6587252016-06-16Arne Goedeke  if (m->stop) { if (m->stop != -1) { m->size=1+m->stop-m->start; } else if (m->instance_size) { m->stop = m->instance_size - 1; m->size = m->instance_size - m->start; } }
1d42552014-09-03Per Hedbor  low_make_response_header(m,send_buf);
d445cd2001-05-31Mirar (Pontus Hagland) 
1d42552014-09-03Per Hedbor  if (m->start) { if( m->file )
55708c2015-08-28Michał Gawron  m->file->seek(m->start, Stdio.SEEK_CUR);
1d42552014-09-03Per Hedbor  else if( m->data ) m->data = ((string)m->data)[m->start..];
d445cd2001-05-31Mirar (Pontus Hagland)  }
1d42552014-09-03Per Hedbor  send_stop = sizeof(send_buf);
d445cd2001-05-31Mirar (Pontus Hagland)  if (request_type=="HEAD") {
1d42552014-09-03Per Hedbor  m->file=0; m->data=0; m->size=0;
d445cd2001-05-31Mirar (Pontus Hagland)  } else if (m->data)
99b3392004-01-24Mirar (Pontus Hagland)  {
1d42552014-09-03Per Hedbor  send_buf->add(m->data);
99b3392004-01-24Mirar (Pontus Hagland)  }
1d42552014-09-03Per Hedbor  if( m->size > 0 ) send_stop+=m->size;
d445cd2001-05-31Mirar (Pontus Hagland)  if (m->file) { send_fd=m->file;
1d42552014-09-03Per Hedbor  send_buf->range_error(0);
d445cd2001-05-31Mirar (Pontus Hagland)  }
6587252016-06-16Arne Goedeke 
c0b2db2012-09-12Bill Welliver  my_fd->set_nonblocking(send_read,send_write,send_close);
d445cd2001-05-31Mirar (Pontus Hagland) }
4aeab22016-12-29Martin Nilsson //! Finishes this request, as in removing timeouts, calling the //! logging callback etc. If @[clean] is given, then the processing of //! this request went fine and all data was sent properly, in which //! case the connection will be reused if keep-alive was //! negotiated. Otherwise the connection will be closed and //! destructed.
38c8e72004-01-24Mirar (Pontus Hagland) void finish(int clean)
d445cd2001-05-31Mirar (Pontus Hagland) {
17b06f2002-02-04Per Hedbor  if( log_cb )
563bd72004-01-11Martin Nilsson  log_cb(this);
1d42552014-09-03Per Hedbor 
06728a2004-01-24Mirar (Pontus Hagland)  remove_call_out(send_timeout);
d2704f2015-07-13Per Hedbor  remove_call_out(connection_timeout); send_buf = 0;
38c8e72004-01-24Mirar (Pontus Hagland) 
1d42552014-09-03Per Hedbor  if (!clean
38c8e72004-01-24Mirar (Pontus Hagland)  || !my_fd || !keep_alive) {
7d8ba82013-11-13Per Hedbor  if (my_fd) { catch(my_fd->close()); destruct(my_fd); my_fd=0; }
38c8e72004-01-24Mirar (Pontus Hagland)  return; } // create new request
e2c7ee2013-09-06Arne Goedeke  this_program r=server_port->request_program();
cc1f1e2013-04-16Markus Ottensmann  r->attach_fd(my_fd,server_port,request_callback,buf,error_callback);
38c8e72004-01-24Mirar (Pontus Hagland)  my_fd=0; // and drop this object
d445cd2001-05-31Mirar (Pontus Hagland) }
1d42552014-09-03Per Hedbor class OutputBuffer {
cb65012014-10-01Martin Nilsson  inherit Stdio.Buffer;
1d42552014-09-03Per Hedbor  int range_error( int n ) { if( send_fd ) {
5d34842014-09-03Per Hedbor  int n = input_from( send_fd, min(128*1024,send_stop-sent-sizeof(this)) );
fdab222014-09-03Per Hedbor  if( n < 128*1024 ) {
1d42552014-09-03Per Hedbor  send_fd = 0;
fdab222014-09-03Per Hedbor  } return !!n;
1d42552014-09-03Per Hedbor  } } }
d445cd2001-05-31Mirar (Pontus Hagland) 
04f2d12008-05-09Martin Nilsson //! Returns the amount of data sent. int sent_data() {
fdab222014-09-03Per Hedbor  return sent;
04f2d12008-05-09Martin Nilsson }
d445cd2001-05-31Mirar (Pontus Hagland) void send_write() {
06728a2004-01-24Mirar (Pontus Hagland)  remove_call_out(send_timeout); call_out(send_timeout,send_timeout_delay);
8688fb2014-10-02Martin Nilsson  int n = send_buf->output_to(my_fd);
fdab222014-09-03Per Hedbor 
749c6b2018-04-08Bill Welliver  // SSL.File->write() does not guarantee that all data has been // written upon return; this can cause premature disconnects. // By waiting for any buffers to empty before calling finish, we // help ensure the full result has been transmitted. if(my_fd->query_version) { if( n == 0 && send_stop == sent) finish(1);
c0d8c12018-08-22Tobias S. Josefowitz  else if(n <= 0) finish(sent == send_stop); else sent += n;
749c6b2018-04-08Bill Welliver  } else { if ( n <= 0 || (send_stop==(sent+=n)) ) finish(sent==send_stop); }
d445cd2001-05-31Mirar (Pontus Hagland) }
06728a2004-01-24Mirar (Pontus Hagland) void send_timeout() {
38c8e72004-01-24Mirar (Pontus Hagland)  finish(0);
06728a2004-01-24Mirar (Pontus Hagland) }
d445cd2001-05-31Mirar (Pontus Hagland) void send_close() { /* socket closed by peer */
38c8e72004-01-24Mirar (Pontus Hagland)  finish(0);
d445cd2001-05-31Mirar (Pontus Hagland) }
06728a2004-01-24Mirar (Pontus Hagland) 
38c8e72004-01-24Mirar (Pontus Hagland) void send_read(mixed dummy,string s)
06728a2004-01-24Mirar (Pontus Hagland) {
fdab222014-09-03Per Hedbor  buf+=s; // for HTTP/1.1
06728a2004-01-24Mirar (Pontus Hagland) }