From 1d4255de138ba96f827dc30f8ce118d6e1aa5d16 Mon Sep 17 00:00:00 2001 From: Per Hedbor <ph@opera.com> Date: Wed, 3 Sep 2014 17:08:43 +0200 Subject: [PATCH] Use Stdio.IOBuffer for output. --- .../HTTP.pmod/Server.pmod/Request.pike | 331 +++++++++--------- 1 file changed, 156 insertions(+), 175 deletions(-) diff --git a/lib/modules/Protocols.pmod/HTTP.pmod/Server.pmod/Request.pike b/lib/modules/Protocols.pmod/HTTP.pmod/Server.pmod/Request.pike index c63b8cc805..e670493d4d 100644 --- a/lib/modules/Protocols.pmod/HTTP.pmod/Server.pmod/Request.pike +++ b/lib/modules/Protocols.pmod/HTTP.pmod/Server.pmod/Request.pike @@ -60,7 +60,7 @@ Port server_port; string buf=""; // content buffer //! raw unparsed full request (headers and body) -string raw=""; +string raw = ""; //! raw unparsed body of the request (@[raw] minus request line and headers) string body_raw=""; @@ -108,6 +108,8 @@ int connection_timeout_delay=180; function(this_program:void) request_callback; function(this_program,array:void) error_callback; +System.Timer startt = System.Timer(); + void attach_fd(Stdio.NonblockingStream _fd, Port server, function(this_program:void) _request_callback, void|string already_data, @@ -118,12 +120,9 @@ void attach_fd(Stdio.NonblockingStream _fd, Port server, headerparser = .HeaderParser(); request_callback=_request_callback; error_callback = _error_callback; - my_fd->set_nonblocking(read_cb,0,close_cb); - call_out(connection_timeout,connection_timeout_delay); - - if (already_data) + if (already_data && strlen(already_data)) read_cb(0,already_data); } @@ -146,7 +145,15 @@ constant singular_headers = ({ constant singular_use_headers = ({ "cookie", "cookie2", -}); +}); + + + +private int sent; +private OutputBuffer send_buf = OutputBuffer(); +private Stdio.File send_fd=0; +private int send_stop; +private int keep_alive=0; protected void flatten_headers() { @@ -166,13 +173,12 @@ protected void flatten_headers() // read callback. protected void read_cb(mixed dummy,string s) { - if( !strlen( raw ) ) - { - while( strlen(s) && (<' ','\t','\n','\r'>)[s[0]] ) - s = s[1..]; - if( !strlen( s ) ) - return; - } + if( !sizeof( raw ) ) + { + sscanf(s,"%*[ \t\n\r]%s", s ); + if( !strlen( s ) ) + return; + } raw+=s; remove_call_out(connection_timeout); array v=headerparser->feed(s); @@ -187,7 +193,7 @@ protected void read_cb(mixed dummy,string s) parse_request(); if (parse_variables()) - finalize(); + finalize(); } else call_out(connection_timeout,connection_timeout_delay); @@ -295,7 +301,6 @@ private void read_cb_chunked( mixed dummy, string data ) buf = buf[2..]; chunked_state = READ_SIZE; break; - case READ_TRAILER: trailers += buf; @@ -350,20 +355,20 @@ protected int parse_variables() flatten_headers(); - if ( request_headers->expect ) + if ( request_headers->expect ) { if ( lower_case(request_headers->expect) == "100-continue" ) my_fd->write("HTTP/1.1 100 Continue\r\n\r\n"); } - if( request_headers["transfer-encoding"] && + if( request_headers["transfer-encoding"] && has_value(lower_case(request_headers["transfer-encoding"]),"chunked")) { my_fd->set_read_callback(read_cb_chunked); read_cb_chunked(0,""); return 0; } - + int l = (int)request_headers["content-length"]; if (l<=sizeof(buf)) { @@ -484,103 +489,124 @@ protected string _sprintf(int t) // ---------------------------------------------------------------- function log_cb; + string make_response_header(mapping m) { + return (string)low_make_response_header(m,Stdio.IOBuffer()); +} + +Stdio.IOBuffer low_make_response_header(mapping m, Stdio.IOBuffer res) +{ + void radd( mixed ... args ) + { + res->add(@args,"\r\n"); + }; + if (protocol!="HTTP/1.0") + { if (protocol=="HTTP/1.1") { // check for fire and forget here and go back to 1.0 then } else protocol="HTTP/1.0"; + } + + 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; + } + } - array(string) res=({}); switch (m->error) { case 0: - case 200: + case 200: if (undefinedp(m->start)) - res+=({protocol+" 200 OK"}); // HTTP/1.1 when supported + radd(protocol," 200 OK"); // HTTP/1.1 when supported else { - res+=({protocol+" 206 Partial content"}); + radd(protocol," 206 Partial content"); m->error=206; } break; default: if(Protocols.HTTP.response_codes[(int)m->error]) - res+=({protocol+" " + Protocols.HTTP.response_codes[(int)m->error]}); + radd(protocol,' ', Protocols.HTTP.response_codes[(int)m->error]); else - res+=({protocol+" "+m->error+" ERROR"}); - break; + radd(protocol,' ',m->error," ERROR"); + break; } if (!m->type) m->type = .filename_to_type(not_query); - res+=({"Content-Type: "+m->type}); - - res+=({"Server: "+(m->server || .http_serverid)}); + if( m->error == 206 ) + radd("Content-Range: bytes ", m->start,"-",m->stop,"/",m->size); - string http_now = .http_date(time(1)); - res+=({"Date: "+http_now}); + radd("Content-Type: ",m->type); - if (!m->stat && m->file) - m->stat=m->file->stat(); + if( m->size >= 0 ) + radd("Content-Length: ",(string)m->size); - if (!m->file && !m->data) - m->data=""; + radd("Server: ", m->server || .http_serverid); + + string http_now = .http_date(time(1)); + radd("Date: ",http_now); if (m->modified) - res+=({"Last-Modified: " + .http_date(m->modified)}); + radd("Last-Modified: ", .http_date(m->modified)); else if (m->stat) - res+=({"Last-Modified: " + .http_date(m->stat->mtime)}); + radd("Last-Modified: ", .http_date(m->stat->mtime)); else - res+=({"Last-Modified: " + http_now}); + radd("Last-Modified: ", http_now); if (m->extra_heads) foreach (m->extra_heads;string name;array|string arr) foreach (Array.arrayify(arr);;string value) - res+=({String.capitalize(name)+": "+value}); + radd(String.capitalize(name),": ",value); // FIXME: insert cookies here? - - if (undefinedp(m->size)) - if (m->data) - m->size=sizeof(m->data); - else if (m->stat) - m->size=m->stat->size; - else - m->size=-1; - - if (m->size!=-1) - { - res+=({"Content-Length: "+m->size}); - 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[0]=protocol+" 416 Requested range not satisfiable"; - - res+=({"Content-Range: bytes "+ - m->start+"-"+m->stop+"/"+m->size}); - } - } - string cc = lower_case(request_headers["connection"]||""); if( (protocol=="HTTP/1.1" && !has_value(cc,"close")) || cc=="keep-alive" ) { - res+=({"Connection: Keep-Alive"}); + radd("Connection: keep-alive"); keep_alive=1; } else - res+=({"Connection: Close"}); + { + radd("Connection: close"); + } - return res*"\r\n"+"\r\n\r\n"; + res->add("\r\n"); + return res; } //! Return the IP address that originated the request, or 0 if @@ -588,10 +614,10 @@ string make_response_header(mapping m) //! error, @[my_fd]@tt{->errno()@} will be set. string get_ip() { - string addr = my_fd?->query_address(); - if (!addr) return 0; - sscanf(addr,"%s ",addr); - return addr; + string addr = my_fd?->query_address(); + if (!addr) return 0; + sscanf(addr,"%s ",addr); + return addr; } //! return a properly formatted response to the HTTP client @@ -629,26 +655,26 @@ void response_and_finish(mapping m, function|void _log_cb) m->start=a,m->stop=b; else if (sscanf(request_headers->range,"bytes%*[ =]-%d",b)==2) { - if( m->size==-1 || m->size < b ) - { + if( m->size==-1 || m->size < b ) + { m_delete(m,"file"); m->data=""; m->error=416; - } - else - { - m->start=m->size-b; - m->stop=-1; - } + } + else + { + m->start=m->size-b; + m->stop=-1; + } } else if (sscanf(request_headers->range,"bytes%*[ =]%d-",a)==2) m->start=a,m->stop=-1; else if (has_value(request_headers->range, ",")) { - // Multiple ranges - m_delete(m,"file"); - m->data=""; - m->error=416; + // Multiple ranges + m_delete(m,"file"); + m->data=""; + m->error=416; } } @@ -670,74 +696,52 @@ void response_and_finish(mapping m, function|void _log_cb) if (request_headers["if-none-match"] && m->extra_heads ) { - 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; - } - } - } + 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; + } + } + } } - string header=make_response_header(m); + low_make_response_header(m,send_buf); if (m->stop) m->size=1+m->stop-m->start; - if (m->file && m->start) m->file->seek(m->start); - - if (m->file && - m->size!=-1 && - m->size+sizeof(header)<4096) // fit in buffer - { - m->data=m->file->read(m->size); - m->file->close(); - m->file=0; + if (m->start) { + if( m->file ) + m->file->seek(m->start+m->tell()); + else if( m->data ) + m->data = ((string)m->data)[m->start..]; } + send_stop = sizeof(send_buf); + if (request_type=="HEAD") { - send_buf=header; - if (m->file) m->file->close(),m->file=0; + m->file=0; + m->data=0; + m->size=0; } else if (m->data) { - if (m->stop) - send_buf=header+m->data[..m->size-1]; - else - send_buf=header+m->data; + send_buf->add(m->data); } - else - send_buf=header; - - if (sizeof(send_buf)<4096 && - !m->file) - { - sent = my_fd->write(send_buf); - send_buf=""; - finish(1); - return; - } - - send_pos=0; - send_stop=strlen(header)+m->size; + if( m->size > 0 ) + send_stop+=m->size; if (m->file) { send_fd=m->file; - send_buf+=send_fd->read(8192); // start read-ahead if possible + send_buf->range_error(0); } - else - send_fd=0; - if (strlen(send_buf)>=send_stop) - send_buf=send_buf[..send_stop-1]; - - call_out(send_timeout,send_timeout_delay); my_fd->set_nonblocking(send_read,send_write,send_close); } @@ -745,14 +749,10 @@ void finish(int clean) { if( log_cb ) log_cb(this); - if (send_fd) { - send_fd->close(); - destruct(send_fd); - send_fd=0; - } + remove_call_out(send_timeout); - if (!clean + if (!clean || !my_fd || !keep_alive) { @@ -768,12 +768,20 @@ void finish(int clean) my_fd=0; // and drop this object } -private int sent; -private string send_buf=""; -private int send_pos; -private Stdio.File send_fd=0; -private int send_stop; -private int keep_alive=0; +class OutputBuffer +{ + inherit Stdio.IOBuffer; + int range_error( int n ) + { + if( send_fd ) + { + int n = input_from( send_fd, 8192 ); + if( n < 8192 ) + send_fd = 0; + return n; + } + } +} //! Returns the amount of data sent. int sent_data() @@ -785,37 +793,10 @@ void send_write() { remove_call_out(send_timeout); call_out(send_timeout,send_timeout_delay); - - if (sizeof(send_buf)-send_pos<8192 && - send_fd) - { - string q; - q=send_fd->read(65536); - if (!q || q=="") - { - send_fd->close(); - send_fd=0; - } - else - { - send_buf=send_buf[send_pos..]+q; - send_pos=0; - - if (strlen(send_buf)+sent>=send_stop) - { - send_buf=send_buf[..send_stop-1-sent]; - send_fd->close(); - send_fd=0; - } - } - } - - int n=my_fd->write(send_buf[send_pos..]); - + /* limit data size (for SSL files) */ + int n=send_buf->output_to(my_fd,16384); sent += n; - send_pos+=n; - - if (send_pos==sizeof(send_buf) && !send_fd) + if ( (send_stop==sent) || n <= 0 ) finish(sent==send_stop); } -- GitLab