exercises

Log | Files | Refs | README

main.rs (4430B)


      1 use std::{
      2     io::{self, Read, Result, Write},
      3     net::TcpStream,
      4 };
      5 
      6 use ffi::Event;
      7 use poll::Poll;
      8 
      9 mod ffi;
     10 mod poll;
     11 
     12 fn get_req(path: &str) -> Vec<u8> {
     13     format!(
     14         "GET {path} HTTP/1.1\r\n\
     15         Host: localhost\r\n\
     16         Connection: close\r\n\
     17         \r\n"
     18     )
     19     .into_bytes()
     20 }
     21 
     22 fn handle_events(events: &[Event], streams: &mut [TcpStream]) -> Result<usize> {
     23     let mut handled_events = 0;
     24     for event in events {
     25         // We retrieve the token that identifies which TcpStream
     26         // we received an event for
     27         let index = event.token();
     28         let mut data = vec![0u8; 4096];
     29 
     30         loop {
     31             match streams[index].read(&mut data) {
     32                 // If we get Ok(n) and the value is 0, we’ve drained the
     33                 // buffer; we consider the event as handled and break out
     34                 // of the loop.
     35                 Ok(0) => {
     36                     handled_events += 1;
     37                     break;
     38                 }
     39                 // If we get Ok(n) with a value larger than 0,
     40                 // we read the data to a String and print it out
     41                 // with some formatting.
     42                 Ok(n) => {
     43                     let txt = String::from_utf8_lossy(&data[..n]);
     44                     println!("RECEIVED: {:?}", event);
     45                     println!("{txt}\n------\n");
     46                 }
     47                 // WouldBlock indicates that the data transfer is not complete,
     48                 // but there is no data ready right now.
     49                 Err(e) if e.kind() == io::ErrorKind::WouldBlock => break,
     50                 Err(e) => return Err(e),
     51             }
     52         }
     53     }
     54 
     55     Ok(handled_events)
     56 }
     57 
     58 fn main() -> Result<()> {
     59     // The first thing we do is to create a new Poll instance.
     60     let mut poll = Poll::new()?;
     61     // We also specify what number of events we want to create
     62     // and handle in our example.
     63     let n_events = 5;
     64 
     65     // The next step is creating a variable to hold a collection of
     66     // Vec<TcpStream> objects.
     67     let mut streams = vec![];
     68     // We also store the address to our local delayserver in
     69     // a variable called addr.
     70     let addr = "localhost:8090";
     71 
     72     // The next part is where we create a set of requests that we issue to
     73     // our delayserver, which will eventually respond to us. For each request,
     74     // we expect a read event to happen sometime later on in the TcpStream
     75     // we sent the request on.
     76     for i in 0..n_events {
     77         // Setting the delay to (n_events - i) * 1000 simply sets the
     78         // first request we make to have the longest timeout,
     79         // so we should expect the responses to arrive in the reverse order
     80         // from which they were sent.
     81         let delay = (n_events - i) * 1000;
     82         let url_path = format!("/{delay}/request-{i}");
     83         let request = get_req(&url_path);
     84         let mut stream = std::net::TcpStream::connect(addr)?;
     85         stream.set_nonblocking(true)?;
     86 
     87         stream.write_all(&request)?;
     88         poll.registry()
     89             .register(&stream, i, ffi::EPOLLIN | ffi::EPOLLET)?;
     90 
     91         streams.push(stream);
     92     }
     93 
     94     // The next part of the main function is handling incoming events:
     95     //
     96     // First, create a variable called handled_events to track
     97     // how many events we have handled.
     98     let mut handled_events = 0;
     99     // We loop as long as the handled events are less than the number
    100     // of events we expect. Once all events are handled, we exit the loop.
    101     while handled_events < n_events {
    102         // It’s important that we create this using Vec::with_capacity
    103         // since the operating system will assume that we pass it
    104         // memory that we’ve allocated.
    105         let mut events = Vec::with_capacity(10);
    106         // this will actually tell the operating system to park our thread
    107         // and wake us up when an event has occurred.
    108         poll.poll(&mut events, None)?;
    109 
    110         if events.is_empty() {
    111             // If we’re woken up, but there are no events in the list,
    112             // it’s either a timeout or a spurious event (which could
    113             // happen, so we need a way to check whether a timeout has
    114             // actually elapsed if that’s important to us).
    115             println!("TIMEOUT (OR SPURIOUS EVENT NOTIFICATION)");
    116             continue;
    117         }
    118 
    119         handled_events += handle_events(&events, &mut streams)?;
    120     }
    121 
    122     println!("FINISHED");
    123     Ok(())
    124 }