Skip to content
Snippets Groups Projects
Select Git revision
  • 2b49c1792d48a99eff57a01bf31a8f5e765115c4
  • master default
  • chacha-poly1305-test
  • rsa-crt-hardening
  • chacha96
  • fat-library
  • versioned-symbols
  • curve25519
  • dsa-reorg
  • aead-api
  • set_key-changes
  • poly1305
  • aes-reorg
  • nettle-2.7-fixes
  • size_t-changes
  • ecc-support
  • experimental-20050201
  • lsh-1.4.2
  • nettle_3.1.1_release_20150424
  • nettle_3.1_release_20150407
  • nettle_3.1rc3
  • nettle_3.1rc2
  • nettle_3.1rc1
  • nettle_3.0_release_20140607
  • nettle_2.7.1_release_20130528
  • nettle_2.7_release_20130424
  • nettle_2.6_release_20130116
  • nettle_2.5_release_20120707
  • converted-master-branch-to-git
  • nettle_2.4_release_20110903
  • nettle_2.3_release_20110902
  • nettle_2.2_release_20110711
  • nettle_2.1_release_20100725
  • camellia_32bit_20100720
  • nettle_2.0_release_20090608
  • nettle_1.15_release_20061128
  • after_experimental_merge_20060516
  • head_before_experimental_merge_20060516
38 results

configure.ac

Blame
  • Forked from Nettle / nettle
    Source project has a limited visibility.
    Request.pike 19.21 KiB
    #pike __REAL_VERSION__
    
    // There are three different read callbacks that can be active, which
    // has the following call graphs. read_cb is the default read
    // callback, installed by attach_fd.
    //
    //   | (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
    //
    //   | (Incoming data)
    //   v
    // read_cb_post
    //   | If enough data has been received
    //   v
    // finalize
    //
    //   | (Incoming data)
    //   v
    // read_cb_chunked
    //   | If all data chunked transfer-encoding needs
    //   v
    // finalize
    
    
    int max_request_size = 0;
    
    void set_max_request_size(int size)
    {
      max_request_size = size;
    }
    
    protected class Port {
      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();
      void destroy();
    }
    
    Stdio.File my_fd;
    Port server_port;
    .HeaderParser headerparser;
    
    string buf="";    // content buffer
    
    //! raw unparsed full request (headers and body)
    string raw="";
    
    //! raw unparsed body of the request (@[raw] minus request line and headers)
    string body_raw="";
    
    //! full request line (@[request_type] + @[full_query] + @[protocol])
    string request_raw;
    
    //! HTTP request method, eg. POST, GET, etc.
    string request_type;
    
    //! full resource requested, including attached GET query
    string full_query;
    
    //! resource requested minus any attached query
    string not_query;
    
    //! query portion of requested resource, starting after the first "?"
    string query;
    
    //! request protocol and version, eg. HTTP/1.0
    string protocol;
    
    int started = time();
    
    //! all headers included as part of the HTTP request, ie content-type.
    mapping(string:string|array(string)) request_headers=([]);
    
    //! all variables included as part of a GET or POST request.
    mapping(string:string|array(string)) variables=([]);
    
    //! cookies set by client
    mapping(string:string) cookies=([]);
    
    //! external use only
    mapping misc=([]);
    
    //! send timeout (no activity for this period with data in send buffer)
    //! in seconds, default is 180
    int send_timeout_delay=180;
    
    //! connection timeout, delay until connection is closed while
    //! waiting for the correct headers:
    int connection_timeout_delay=180;
    
    function(this_program:void) request_callback;
    function(this_program,array:void) error_callback;
    
    void attach_fd(Stdio.File _fd, Port server,
    	       function(this_program:void) _request_callback,
    	       void|string already_data,
    	       void|function(this_program,array:void) _error_callback)
    {
       my_fd=_fd;
       server_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)
          read_cb(0,already_data);
    }
    
    
    // Some (wap-gateways, specifically) servers send multiple
    // content-length, as an example..
    constant singular_headers = ({
        "content-length",
        "content-type",
        "connection",
        "transfer-encoding",
        "if-modified-since",
        "date",
        "pragma",
        "range",
    });
    
    // We use these as if we only got one, regardless of the number of
    // headers received.
    constant singular_use_headers = ({
        "cookie",
        "cookie2",
    });    
    
    protected void flatten_headers()
    {
      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]*";";
    }
    
    // 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.
    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;
        }
       raw+=s;
       remove_call_out(connection_timeout);
       array v=headerparser->feed(s);
       if (v)
       {
          destruct(headerparser);
          headerparser=0;
          buf=v[0];
          request_headers=v[2];
    
          request_raw=v[1];
          parse_request();
    
          if (parse_variables())
    	finalize();
       }
       else
          call_out(connection_timeout,connection_timeout_delay);
    }
    
    protected void connection_timeout()
    {
       finish(0);
    }
    
    // Parses the request and populates request_type, protocol,
    // full_query, query and not_query.
    protected void parse_request()
    {
       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];
                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);
                }
    	    full_query=v[1..<1]*" ";
    	    break;
    	 }
             // Fallthrough
          case 2:
    	 request_type=v[0];
    	 protocol="HTTP/0.9";
    	 full_query=v[1..]*" ";
    	 break;
       }
    
       query = "";
       not_query = full_query;
       sscanf(full_query, "%s?%s", not_query, query);
    }
    
    enum ChunkedState {
        READ_SIZE = 1,
        READ_CHUNK,
        READ_POSTNL,
        READ_TRAILER,
        FINISHED,
    };
    
    private ChunkedState chunked_state = READ_SIZE;
    private int chunk_size;
    private string current_chunk = "";
    private string actual_data = "";
    private string trailers = "";
    
    // 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.
    private void read_cb_chunked( mixed dummy, string data )
    {
      raw += data;
      buf += data;
      remove_call_out(connection_timeout);
      while( chunked_state == FINISHED || strlen( buf ) )
      {
        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;
    	break;
    
          case READ_CHUNK:
    	int l = min( strlen(buf), chunk_size );
    	chunk_size -= l;
    	actual_data += buf[..l-1];
    	buf = buf[l..];
    	if( !chunk_size )
    	  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;
    	
    
          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
    	  {
    	    buf = buf[2..];
    	    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));
    	    hv = String.trim_whites(hv);
    	    if( request_headers[hk] )
    	    {
    	      if( !arrayp( request_headers[hk] ) )
    		request_headers[hk] = ({request_headers[hk]});
    	      request_headers[hk]+=({hv});
    	    }
    	    else
    	      request_headers[hk] = hk;
    	  }
    	}
    
    
    	// And FINALLY we are done..
    	body_raw = actual_data;
    	request_headers["content-length"] = ""+strlen(actual_data);
    	finalize();
    	return;
        }
      }
      call_out(connection_timeout,connection_timeout_delay);
    }
    
    protected int parse_variables()
    {
      if (query!="")
        .http_decode_urlencoded_query(query,variables);
    
      flatten_headers();
    
      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"] && 
          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))
      {
        int zap = strlen(buf) - l;
        raw = raw[..<zap-1];
        body_raw = buf[..l-1];
        buf = buf[l..];
        return 1;
      }
      else if (request_type == "PUT" )
      {
        body_raw = buf;
        return 1; // do not read body when method is PUT
      }
    
      my_fd->set_read_callback(read_cb_post);
      return 0; // delay
    }
    
    protected void parse_post()
    {
      if ( request_headers["content-type"] && 
           has_prefix(request_headers["content-type"], "multipart/form-data") )
      {
        MIME.Message messg = MIME.Message(body_raw, request_headers, 0, 1);
        if(!messg->body_parts) return;
    
        foreach(messg->body_parts, object part) {
          if(part->disp_params->filename) {
    
    	      if(variables[part->disp_params->name] && !arrayp(variables[part->disp_params->name]))
    	        variables[part->disp_params->name] = ({ variables[part->disp_params->name] });
    	
    	      if(variables[part->disp_params->name] && arrayp(variables[part->disp_params->name]))
    	        variables[part->disp_params->name] += ({part->getdata()});
    	      else variables[part->disp_params->name]=part->getdata();
    
    	      if(variables[part->disp_params->name+".filename"] && !arrayp(variables[part->disp_params->name+".filename"]))
    	        variables[part->disp_params->name+".filename"] = ({ variables[part->disp_params->name+".filename"] });
    	
    	      if(variables[part->disp_params->name+".filename"] && arrayp(variables[part->disp_params->name+".filename"]))
    	        variables[part->disp_params->name+".filename"] += ({part->disp_params->filename});
    	      else
     	        variables[part->disp_params->name+".filename"]= part->disp_params->filename;      
     	        
     	      if(variables[part->disp_params->name+".mimetype"] && !arrayp(variables[part->disp_params->name+".mimetype"]))
      	      variables[part->disp_params->name+".mimetype"] = ({ variables[part->disp_params->name+".mimetype"] });
    
      	    if(variables[part->disp_params->name+".mimetype"] && arrayp(variables[part->disp_params->name+".mimetype"]))
      	      variables[part->disp_params->name+".mimetype"] += ({part->disp_params->filename});
      	    else
       	      variables[part->disp_params->name+".mimetype"]= part->disp_params->filename;      
      	    
    	    } 
    	    else
    	      variables[part->disp_params->name] = part->getdata();
        }
      }
      else if( request_headers["content-type"] &&
    	   ( has_value(request_headers["content-type"], "url-encoded") ||
                 has_value(request_headers["content-type"], "urlencoded") ))
    
      {
        .http_decode_urlencoded_query(body_raw,variables);
      }
    }
    
    protected void finalize()
    {
      my_fd->set_blocking();
      flatten_headers();
      if (array err = catch {parse_post();})
      {
        if (error_callback) error_callback(this, err);
      }
      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;
        request_callback(this);
      }
    }
    
    // Adds incoming data to raw and buf. Once content-length or
    // max_request_size data has been received, finalize is called.
    protected void read_cb_post(mixed dummy,string s)
    {
      raw += s;
      buf += s;
      remove_call_out(connection_timeout);
    
      int l = (int)request_headers["content-length"];
      if (sizeof(buf)>=l ||
          ( max_request_size && sizeof(buf)>max_request_size ))
      {
        body_raw=buf[..l-1];
        buf = buf[l..];
        raw = raw[..strlen(raw)-strlen(buf)];
        finalize();
      }
      else
        call_out(connection_timeout,connection_timeout_delay);
    }
    
    protected void close_cb()
    {
    // closed by peer before request read
       if (my_fd) { my_fd->close(); destruct(my_fd); my_fd=0; }
    }
    
    protected string _sprintf(int t)
    {
      return t=='O' && sprintf("%O(%O %O)",this_program,request_type,full_query);
    }
    
    // ----------------------------------------------------------------
    function log_cb;
    string make_response_header(mapping m)
    {
       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";
    
       array(string) res=({});
       switch (m->error)
       {
          case 0:
          case 200: 
    	 if (zero_type(m->start))
    	    res+=({protocol+" 200 OK"}); // HTTP/1.1 when supported
    	 else
    	 {
    	    res+=({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]});
             else
              res+=({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)});
    
       string http_now = .http_date(time(1));
       res+=({"Date: "+http_now});
    
       if (!m->stat && m->file)
          m->stat=m->file->stat();
    
       if (!m->file && !m->data)
          m->data="";
    
       if (m->modified)
          res+=({"Last-Modified: " + .http_date(m->modified)});
       else if (m->stat)
          res+=({"Last-Modified: " + .http_date(m->stat->mtime)});
       else
          res+=({"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});
    
    // FIXME: insert cookies here?
    
       if (zero_type(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 (!zero_type(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"});
           keep_alive=1;
       }
       else
           res+=({"Connection: Close"});
    
       return res*"\r\n"+"\r\n\r\n";
    }
    
    //! return a properly formatted response to the HTTP client
    //! @param m 
    //! 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
    //! @member int "length"
    //!   length of content returned. If @i{file@} is provided, @i{size@} 
    //!   bytes will be returned to client.
    //! @member string "modified"
    //!   contains optional modification date.
    //! @member string "type"
    //!   contains optional content-type
    //! @member mapping "extra_heads"
    //!   contains a mapping of additional headers to be 
    //! returned to client.
    //! @member string "server" 
    //!   contains the server identification header.
    //! @endmapping
    void response_and_finish(mapping m, function|void _log_cb)
    {
       m += ([ ]);
       log_cb = _log_cb;
    
       if (request_headers->range && !m->start && zero_type(m->error))
       {
          int a,b;
          if (sscanf(request_headers->range,"bytes%*[ =]%d-%d",a,b)==3)
    	 m->start=a,m->stop=b;
          else if (sscanf(request_headers->range,"bytes%*[ =]-%d",b)==2)
          {
            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 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;
          }
       }
    
       if (request_headers["if-modified-since"])
       {
          int t = .http_decode_date(request_headers["if-modified-since"]);
          if (t)
          {
    	 if (!m->stat && m->file)
    	    m->stat=m->file->stat();
    	 if (m->stat && m->stat->mtime<=t)
    	 {
    	    m_delete(m,"file");
    	    m->data="";
    	    m->error=304;
    	 }
          }
       }
    
       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 header=make_response_header(m);
    
       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 (request_type=="HEAD")
       {
          send_buf=header;
          if (m->file) m->file->close(),m->file=0;
       }
       else if (m->data)
       {
          if (m->stop)
    	 send_buf=header+m->data[..m->size-1];
          else
    	 send_buf=header+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->file)
       {
          send_fd=m->file;
          send_buf+=send_fd->read(8192); // start read-ahead if possible
       }
       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);
    }
    
    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 
           || !my_fd
           || !keep_alive)
       {
          if (my_fd) { my_fd->close(); destruct(my_fd); my_fd=0; }
          return;
       }
    
       // create new request
    
       this_program r=this_program();
       r->attach_fd(my_fd,server_port,request_callback,buf,error_callback);
    
       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;
    
    //! Returns the amount of data sent.
    int sent_data()
    {
      return sent;
    }
    
    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..]);
    
       sent += n;
       send_pos+=n;
    
       if (send_pos==sizeof(send_buf) && !send_fd)
          finish(sent==send_stop);
    }
    
    void send_timeout()
    {
       finish(0);
    }
    
    void send_close()
    {
    /* socket closed by peer */
       finish(0);
    }
    
    void send_read(mixed dummy,string s)
    {
      buf+=s; // for HTTP/1.1
    }