Skip to content

Commit

Permalink
Make httpget app work
Browse files Browse the repository at this point in the history
  • Loading branch information
hikalium committed Sep 2, 2024
1 parent aab855c commit 1c2c5c0
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 63 deletions.
13 changes: 7 additions & 6 deletions app/httpget/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn main() -> Result<()> {
}
let host = &args[1];

let host = match lookup_host(host) {
let ip = match lookup_host(host) {
Ok(results) => {
if let Some(host) = results.first() {
*host
Expand All @@ -35,10 +35,11 @@ fn main() -> Result<()> {
}
};
let port = 80;
let socket_addr: SocketAddr = (host, port).into();
let socket_addr: SocketAddr = (ip, port).into();
let mut stream = TcpStream::connect(socket_addr)?;
println!("stream: {stream:?}");
let bytes_written = stream.write(format!("GET / HTTP/1.1\nHost: {host}\n\n").as_bytes())?;
let bytes_written =
stream.write(format!("GET / HTTP/1.1\nHost: {host}\nConnection: Close\n\n").as_bytes())?;
println!("bytes_written = {bytes_written}");
let mut received = Vec::new();
loop {
Expand All @@ -49,9 +50,9 @@ fn main() -> Result<()> {
break;
}
received.extend_from_slice(&buf[..bytes_read]);
}
if let Ok(received) = core::str::from_utf8(&received) {
println!("{received}");
if let Ok(received) = core::str::from_utf8(&received) {
println!("{received}");
}
}
Ok(())
}
Expand Down
59 changes: 24 additions & 35 deletions noli/src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@ use crate::error::Error;
use crate::error::Result;
use crate::mem::Sliceable;
use crate::prelude::*;
use crate::print::hexdump;
use crate::println;
use alloc::fmt;
use alloc::fmt::Debug;
use alloc::fmt::Display;
use alloc::string::String;
use alloc::vec::Vec;
use core::cmp::min;
use core::convert::From;
use core::str::FromStr;
use sabi::RawIpV4Addr;
Expand Down Expand Up @@ -81,33 +78,10 @@ impl From<(IpV4Addr, u16)> for SocketAddr {
}
}

static FAKE_TCP_DATA: &str = r#"
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Date: Mon, 15 Jan 2024 10:05:49 GMT
Content-Length: 433
<!doctype html>
<html>
<head>
<title>Example Domain Response</title>
<meta charset="utf-8" />
</head>
<body>
<div>
<h1>Example Domain Response</h1>
<p>This domain is for use in illustrative examples in documents. You may use this
domain in literature without prior coordination or asking for permission.</p>
<p><a href="https://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>
"#;

#[derive(Debug)]
pub struct TcpStream {
sock_addr: SocketAddr,
_handle: i64,
handle: i64,
}
impl TcpStream {
pub fn sock_addr(&self) -> &SocketAddr {
Expand All @@ -118,28 +92,43 @@ impl TcpStream {
if handle >= 0 {
Ok(Self {
sock_addr: sa,
_handle: handle,
handle,
})
} else {
Err(Error::Failed("Failed to open TCP socket"))
}
}
pub fn write(&mut self, buf: &[u8]) -> Result<usize> {
// There's no core::io::Write trait so implement this directly.
println!("TcpStream::write: {self:?}");
hexdump(buf);
Ok(buf.len())
let bytes_written = Api::write_to_tcp_socket(self.handle, buf);
if bytes_written >= 0 {
Ok(bytes_written as usize)
} else {
let err = bytes_written;
match err {
-1 => Err(Error::Failed("NO_SUCH_SOCKET")),
-2 => Err(Error::Failed("WRITE_ERROR")),
_ => Err(Error::Failed("UNDEFINED")),
}
}
}
/// Reads data received so far with this stream up to the size of `buf`.
/// Returns the size of the data read by this call.
/// This function blocks the execution until there are some data available.
/// This function returns 0 once the connection is closed.
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
// There's no core::io::Read trait so implement this directly.
println!("TcpStream::read: {self:?}");
let copy_size = min(buf.len(), FAKE_TCP_DATA.len());
buf[..copy_size].copy_from_slice(&FAKE_TCP_DATA.as_bytes()[..copy_size]);
Ok(copy_size)
let bytes_written = Api::read_from_tcp_socket(self.handle, buf);
if bytes_written >= 0 {
Ok(bytes_written as usize)
} else {
let err = bytes_written;
match err {
-1 => Err(Error::Failed("NO_SUCH_SOCKET")),
-2 => Err(Error::Failed("READ_ERROR")),
_ => Err(Error::Failed("UNDEFINED")),
}
}
}
}

Expand Down
14 changes: 13 additions & 1 deletion noli/src/sys/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,21 @@ pub trait SystemApi {
}
unimplemented!()
}
/// Returns non-negative handle for the socket.
/// Returns a non-negative handle for the socket.
/// -1: OPEN_FAILED
fn open_tcp_socket(_ip: RawIpV4Addr, _port: u16) -> i64 {
unimplemented!()
}
/// Returns a non-negative byte size that is queued to be sent.
/// -1: NO_SUCH_SOCKET
/// -2: WRITE_ERROR
fn write_to_tcp_socket(_handle: i64, _buf: &[u8]) -> i64 {
unimplemented!()
}
/// Returns a non-negative byte size that is written to the given buffer.
/// -1: NO_SUCH_SOCKET
/// -2: READ_ERROR
fn read_from_tcp_socket(_handle: i64, _buf: &mut [u8]) -> i64 {
unimplemented!()
}
}
8 changes: 7 additions & 1 deletion noli/src/sys/wasabi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ impl SystemApi for Api {
) as i64
}
fn open_tcp_socket(ip: RawIpV4Addr, port: u16) -> i64 {
syscall_2(8, u32::from_le_bytes(ip) as u64, port as u64) as i64
syscall_2(8, u32::from_be_bytes(ip) as u64, port as u64) as i64
}
fn write_to_tcp_socket(handle: i64, buf: &[u8]) -> i64 {
syscall_3(9, handle as u64, buf.as_ptr() as u64, buf.len() as u64) as i64
}
fn read_from_tcp_socket(handle: i64, buf: &mut [u8]) -> i64 {
syscall_3(10, handle as u64, buf.as_mut_ptr() as u64, buf.len() as u64) as i64
}
}
18 changes: 15 additions & 3 deletions os/src/net/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ pub struct TcpSocket {
state: Mutex<TcpSocketState>,
rx_data: Mutex<VecDeque<u8>>,
tx_data: Mutex<VecDeque<u8>>,
keep_listening: bool,
}
impl Debug for TcpSocket {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
Expand All @@ -169,6 +170,7 @@ impl TcpSocket {
state: Mutex::new(TcpSocketState::Listen),
rx_data: Default::default(),
tx_data: Default::default(),
keep_listening: true,
}
}
pub fn new_client(dst_ip: IpV4Addr, dst_port: u16) -> Self {
Expand All @@ -183,6 +185,7 @@ impl TcpSocket {
state: Mutex::new(TcpSocketState::SynSent),
rx_data: Default::default(),
tx_data: Default::default(),
keep_listening: false,
}
}
pub fn self_ip(&self) -> Option<IpV4Addr> {
Expand Down Expand Up @@ -374,8 +377,11 @@ impl TcpSocket {
TcpSocketState::LastAck => {
if in_tcp.is_ack() {
info!("net: tcp: recv: TCP connection closed");
// Return to Listen state
*self.state.lock() = TcpSocketState::Listen;
if self.keep_listening {
*self.state.lock() = TcpSocketState::Listen;
} else {
*self.state.lock() = TcpSocketState::Closed;
}
return Ok(());
}
}
Expand All @@ -402,7 +408,7 @@ impl TcpSocket {
Ok(())
}
pub fn poll_tx(&self) -> Result<()> {
if self.tx_data.lock().is_empty() {
if self.tx_data.lock().is_empty() || !self.is_established() {
return Ok(());
}
let to_ip = self
Expand Down Expand Up @@ -481,4 +487,10 @@ impl TcpSocket {
pub fn is_established(&self) -> bool {
*self.state.lock() == TcpSocketState::Established
}
pub fn is_trying_to_connect(&self) -> bool {
matches!(
*self.state.lock(),
TcpSocketState::SynSent | TcpSocketState::SynReceived
)
}
}
37 changes: 26 additions & 11 deletions os/src/process.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
extern crate alloc;

use crate::error::Error;
use crate::error::Result;
use crate::memory::ContiguousPhysicalMemoryPages;
use crate::mutex::Mutex;
use crate::net::manager::Network;
use crate::net::tcp::TcpSocket;
use crate::x86_64::context::unchecked_load_context;
use crate::x86_64::context::unchecked_switch_context;
use crate::x86_64::context::ExecutionContext;
use crate::x86_64::paging::PageAttr;
use alloc::boxed::Box;
use alloc::collections::btree_map;
use alloc::collections::BTreeMap;
use alloc::collections::VecDeque;
use alloc::rc::Rc;
use core::future::Future;
Expand All @@ -18,6 +23,7 @@ use core::sync::atomic::Ordering;
use core::task::Context;
use core::task::Poll;
use noli::args::serialize_args;
use noli::net::IpV4Addr;

// To take ROOT_SCHEDULER, use Scheduler::root()
static ROOT_SCHEDULER: Scheduler = Scheduler::new();
Expand All @@ -28,23 +34,15 @@ pub fn init() {
ROOT_SCHEDULER.schedule(ProcessContext::default()); // context for current
}

#[derive(Default)]
pub struct ProcessContext {
args_region: Option<ContiguousPhysicalMemoryPages>,
stack_region: Option<ContiguousPhysicalMemoryPages>,
context: Mutex<ExecutionContext>,
exited: Rc<AtomicBool>,
exit_code: Rc<AtomicI64>,
}
impl Default for ProcessContext {
fn default() -> Self {
Self {
args_region: None,
stack_region: None,
context: Mutex::new(ExecutionContext::default()),
exited: Rc::new(AtomicBool::new(false)),
exit_code: Rc::new(AtomicI64::new(0)),
}
}
tcp_sockets: BTreeMap<i64, Rc<TcpSocket>>,
next_tcp_socket_handle: i64,
}
impl ProcessContext {
pub fn new(
Expand Down Expand Up @@ -91,6 +89,23 @@ impl ProcessContext {
pub fn args_region_start_addr(&self) -> Option<usize> {
self.args_region.as_ref().map(|ar| ar.range().start())
}
// Create a new tcp socket and issue a handle for it
pub fn create_tcp_socket(&mut self, ip: IpV4Addr, port: u16) -> Result<i64> {
let network = Network::take();
let sock = network.open_tcp_socket(ip, port)?;
for handle in core::cmp::max(0, self.next_tcp_socket_handle)..=i64::MAX {
if let btree_map::Entry::Vacant(e) = self.tcp_sockets.entry(handle) {
e.insert(sock.clone());
self.next_tcp_socket_handle = self.next_tcp_socket_handle.wrapping_add(1);
assert!(handle >= 0);
return Ok(handle);
}
}
Err(Error::Failed("No more tcp_socket handle available"))
}
pub fn tcp_socket(&self, handle: i64) -> Option<Rc<TcpSocket>> {
self.tcp_sockets.get(&handle).cloned()
}
}

pub struct Scheduler {
Expand Down
Loading

0 comments on commit 1c2c5c0

Please sign in to comment.