Fix typos, add some code

This commit is contained in:
Marko Korhonen 2020-05-09 12:05:12 +03:00
parent 83da9d31cc
commit 6fc4750cdc
No known key found for this signature in database
GPG key ID: 911B85FBC6003FE5
15 changed files with 155 additions and 78 deletions

View file

@ -52,6 +52,7 @@ async fn api_404_unconfigured() -> HttpResponse {
HttpResponse::NotFound().finish()
}
#[rustfmt::skip]
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "actix_web=debug,diesel=debug");
@ -91,14 +92,41 @@ async fn main() -> std::io::Result<()> {
.service(api_404)
.service(
scope("/api/auth")
.service(resource("/register").route(post().to(authentication::register)))
.service(resource("/login").route(post().to(authentication::login)))
.service(resource("/logout").route(post().to(authentication::logout)))
.service(resource("/delete").route(delete().to(authentication::delete))),
.service(
resource("/register")
.route(post()
.to(authentication::register)
)
)
.service(
resource("/login")
.route(post()
.to(authentication::login)
)
)
.service(
resource("/logout")
.route(post()
.to(authentication::logout)
))
.service(
resource("/delete")
.route(delete()
.to(authentication::delete)
)
)
)
.service(
resource("/api/protected")
.route(get()
.to(protected::protected_route)
)
)
.service(resource("/api/protected").route(get().to(protected::protected_route)))
.service(api_404_unconfigured)
.service(Files::new("/", "./static").index_file("index.html"))
.service(
Files::new("/", "./static")
.index_file("index.html")
)
.default_service(get().to(serve_index_html))
})
.bind(address)?

View file

@ -3,7 +3,7 @@
% Acronyms, abbreviations, etc.
\newacronym{html}{HTML}{HyperText Markup Language. Verkossa käytettävä tiedostomuoto.}
\newacronym{html}{HTML}{HyperText Markup Language. Verkossa käytettävä tiedostomuoto}
\newacronym{http}{HTTP}{
Hypertext Transfer Protocol. Alunperin HTML-dokumenttien välittämiseen kehitetty protokolla. Nykyään käytetään paljon muuhunkin
}

View file

@ -32,9 +32,9 @@
Insinöörityössä käydään läpi web-ohjelmoinnin perusteet, sekä Rustin pääominaisuudet. Lukijalta oletetaan hyvin vähän tietämystä ja kaikki olennaiset käsitteet käydään läpi perusteista alkaen.\newline
Insinöörityön yhteydessä tehtiin projekti, missä palvelin- ja asiakaspuoli toteutettiin molemmat Rustilla. Tämän pohjalta arvioitiin kielen soveltuvuutta web-ojelmointiin. Projektin kaikki osa-alueet ja kaikki käytetyt riippuvaisuudet, sekä syyt niiden valitsemiseen on käyty läpi perusteellisesti. Myös kehitysympäristön asentaminen ja projektin aloittaminen on käyty läpi alusta alkaen.\newline
Insinöörityön yhteydessä tehtiin projekti, missä palvelin- ja asiakaspuoli toteutettiin molemmat Rustilla. Projektin kaikki osa-alueet ja kaikki käytetyt riippuvaisuudet, sekä syyt niiden valitsemiseen on käyty läpi perusteellisesti. Myös kehitysympäristön asentaminen ja projektin aloittaminen on käyty läpi alusta alkaen.\newline
Projektista kerätyn käytännön tiedon avulla on arvioitu Rustin soveltuvuutta web-ohjelmointiin erikseen sekä asiakas- että palvelinpuolella. Lopuksi on siirretty katsetta hieman tulevaan ja arvioitu, miten soveltuvuus tulee todennäköisesti muuttumaan tulevaisuudessa. Lopussa on myös suosituksia Rustin sisällyttämisestä uusiin ja olemassa oleviin web-ohjelmointiprojekteihin.
Projektista kerätyn käytännön tiedon avulla on arvioitu Rustin soveltuvuutta web-ohjelmointiin erikseen sekä asiakas- että palvelinpuolella. Lopuksi on siirretty katsetta hieman tulevaan ja arvioitu, miten kielen soveltuvuus tulee todennäköisesti muuttumaan tulevaisuudessa. Lopussa on myös suosituksia Rustin sisällyttämisestä uusiin ja olemassa oleviin web-ohjelmointiprojekteihin.
} \\[14cm] \hline
Avainsanat & \avainsanat

View file

@ -19,9 +19,9 @@
\def\kohjelma{Tieto- ja tietoviestintätekniikka}
\def\suuntautumis{Ohjelmistotuotanto}
\def\ohjaajat{
Simo Silander, tutkintovastaava
Lehtori Simo Silander
}
\def\avainsanat{Rust, web, ohjelmointi, fullstack}
\def\avainsanat{Rust, WebAssembly, ohjelmointi, web, full-stack}
\def\pvm{\specialdate\today}
%English section, for title/abstract
@ -32,5 +32,5 @@
\def\metropoliainstructors {
Simo Silander, Senior Lecturer
}
\def\metropoliakeywords {Keywords}
\def\metropoliakeywords {Rust, WebAssembly, programming, web, full-stack}
\date{\longmonth\today}

View file

@ -1,14 +1,14 @@
% Johdanto
\chapter{Johdanto}
Älylaitteiden ja tietokoneiden kehityksen sekä internetin parantuneen saatavuuden seurauksena netin kautta käytettävien palveluiden kysyntä ja tarjonta ovat kasvaneet todella kovalla vauhdilla viime vuosina. Tästä johtuen koko internetin luonne on muuttunut täysin. Olemme todella kaukana 90-luvun dokumenttipohjaisesta internetistä. Nykyaikaiset nettisivut ovat täysiverisiä ohjelmia.
Älylaitteiden ja tietokoneiden kehityksen sekä internetin parantuneen saatavuuden seurauksena verkon kautta käytettävien palveluiden kysyntä ja tarjonta ovat kasvaneet todella kovalla vauhdilla viime vuosina. Tästä johtuen koko internetin luonne on muuttunut täysin. Olemme todella kaukana 90-luvun dokumenttipohjaisesta internetistä. Nykyaikaiset nettisivut ovat täysiverisiä ohjelmia.
Tämän takia myös web-ohjelmoinnissa käytettävissä teknologioissa on tapahtunut paljon muutoksia. Jotta nettisivuista saatiin pelkkien HTML-dokumenttien sijasta ohjelmia, 90-luvun puolivälissä julkaistiin PHP (palvelinpuolen kieli) ja JavaScript (asiakaspuolen kieli). Tänä päivänä näistä kahdesta JavaScript on ehdottomasti suositumpi, minkä takia keskityn tässä opinnäytetyössä sen vertailuun Rustin kanssa.
Tämän takia myös web-ohjelmoinnissa käytettävissä teknologioissa on tapahtunut paljon muutoksia. Jotta nettisivuista saatiin pelkkien HTML-dokumenttien sijasta ohjelmia, 90-luvun puolivälissä julkaistiin PHP (palvelinpuolen kieli) ja JavaScript (asiakaspuolen kieli). Tänä päivänä näistä kahdesta JavaScript on ehdottomasti suositumpi, minkä takia keskityn tässä insinöörityössä sen vertailuun Rustin kanssa.
Vaikka JavaScript onkin suosittu, siinä on paljon ongelmia\cite{medium:javascript}. Suurimmat ongelmat liittyvät kielen heikkoon tyypitykseen, mikä saattaa aiheuttaa ongelmia, jotka ilmenevät vasta ajon aikana ja vain tietyissä rajatapauksissa. Tämän takia koodin testaamiseen ja laadun ylläpitämiseen pitää panostaa enemmän. Toinen iso ongelma on se, että JavaScript ei ole tarkka koodin tyylistä. Jo pelkästään funktion kirjoittamiseen on useita eri tyylejä. Tämä hankaloittaa projekteissa kommunikointia ja työtoverien koodin lukemista.
Vaikka JavaScript onkin suosittu, siinä on paljon ongelmia\cite{medium:javascript}. Suurimmat ongelmat liittyvät kielen heikkoon tyypitykseen. Se saattaa aiheuttaa ongelmia, jotka ilmenevät vasta ajon aikana ja vain tietyissä rajatapauksissa. Tämän takia koodin testaamiseen ja laadun ylläpitämiseen pitää panostaa enemmän. Toinen iso ongelma on se, että JavaScript ei ole tarkka koodin tyylistä. Jo pelkästään funktion kirjoittamiseen on useita eri tyylejä. Tämä hankaloittaa projekteissa kommunikointia ja työtoverien koodin lukemista.
Koska JavaScript on tulkattava kieli, se tulee aina häviämään suorituskyvyssä käännettäville kielille. Tätä eroa on osittain kavennettu ajonaikaisen kääntämisen optimoinneilla.
Edellä mainittujen syiden takia lähivuosina on ilmaantunut useita vaihtoehtoja sekä palvelin- että asiakaspuolen ohjelmointikieliksi. Näistä yksi itseäni eniten kiinnostava on Rust. Tämän opinnäytetyön yhteydessä toteutettu projekti käyttää Rustia sekä palvelimella että asiakaspuolella.
Edellä mainittujen syiden takia lähivuosina on ilmaantunut useita vaihtoehtoja sekä palvelin- että asiakaspuolen ohjelmointikieliksi. Näistä yksi itseäni eniten kiinnostava on Rust. Tämän insinöörityön yhteydessä toteutettu projekti käyttää Rustia sekä palvelimella että asiakaspuolella.
\clearpage

View file

@ -3,20 +3,24 @@ Web-ohjelmoinnilla tarkoitetaan nettiselaimen välityksellä käytettävien ohje
\section{Asiakaspuoli}
\label{sect:asiakaspuoli}
Asiakaspuoli koostuu käyttäjän nettiselaimesta, missä käyttöliittymän rakenne määritetään HTML-dokumentissa. HTML-dokumenttia voi muokata käyttämällä DOM:ia \cite{wiki:dom}. DOM, eli dokumenttioliomalli on tapa kuvata HTML-dokumenttia puurakenteena, joka mahdollistaa dokumentin elementtien lukemisen ja muokkaamisen. Muokkaaminen suoritetaan asiakaspuolen web-ohjelmointikielellä, johon on tällä hetkellä kaksi vaihtoehtoa: JavaScript sekä WebAssembly, mitä tarkastellaan tässä opinnäytetyössä tarkemmi.
Asiakaspuoli koostuu käyttäjän nettiselaimesta, missä käyttöliittymän rakenne määritetään HTML-dokumentissa. HTML-dokumenttia voi muokata käyttämällä DOM:ia \cite{wiki:dom}. DOM, eli dokumenttioliomalli on tapa kuvata HTML-dokumenttia puurakenteena, joka mahdollistaa dokumentin elementtien lukemisen ja muokkaamisen. Muokkaaminen suoritetaan asiakaspuolen web-ohjelmointikielellä, johon on tällä hetkellä kaksi vaihtoehtoa: JavaScript sekä WebAssembly, jota tarkastellaan tässä insinöörityössä tarkemmin.
Käyttöliittymän ulkonäköä ja toiminnallisuutta voi myös muokata palvelinpuolella. Tätä kutsutaan palvelinpuolen renderöinniksi ja siihen useimmiten käytetty ohjelmointikieli on PHP. Palvelinpuolen renderöinti on kuitenkin jäänyt taka-alalle yhden sivun ohjelmien yleistyessä.
Käyttöliittymän ulkonäköä ja toiminnallisuutta voi myös muokata palvelinpuolella. Tätä kutsutaan palvelinpuolen renderöinniksi ja siihen useimmiten käytetty ohjelmointikieli on PHP. Kun käyttöliittymän ulkoasua muokataan PHP:lla, HTML-dokumenttia muokataan palvelimella ennen kuin se lähetetään asiakkaalle. Palvelinpuolen renderöinti on kuitenkin jäänyt taka-alalle yhden sivun ohjelmien yleistyessä.
\subsection{Yhden sivun ohjelmat}
\section{Yhden sivun ohjelmat}
\label{sect:spa}
Yhden sivun ohjelmat \cite{wiki:spa} ovat tapa toteuttaa asiakaspuolen ohjelmia, missä selain lataa yhden ohjelman ja HTML-dokumentin, minkä jälkeen sivujen uudelleenlatauksia ei enää tapahdu. HTML-dokumentin DOM:ia muutetaan niin sanotusti lennosta.
Yhden sivun ohjelmat \cite{wiki:spa} ovat tapa toteuttaa asiakaspuolen ohjelmia, missä selain lataa yhden ohjelman ja HTML-dokumentin, minkä jälkeen sivujen uudelleenlatauksia ei enää tapahdu. HTML-dokumentin DOM:ia muutetaan niin sanotusti lennosta. Tämä mahdollistaa sulavamman käyttökokemuksen asiakkaan näkökulmasta, sekä helpottaa sovelluksen tilanhallintaa kehittäjän näkökulmasta.
\clearpage
Ohjelman tilaa hallinnoidaan usein lukemalla ja muuttamalla asiakkaan nettiselaimen osoitekenttää. Tätä kutsutaan asiakaspuolen reititykseksi. Reititys mahdollistaa myös tiettyyn ohjelman tilaan siirtymiseen suoraan, mikä on hyödyllistä esimerkiksi silloin, kun sovelluksen käyttäjillä on tarve jakaa linkkejä ohjelmaan.
\section{Palvelinpuoli}
Palvelinpuolella tarkoitetaan ohjelman osaa, mihin asiakaspuoli yhdistyy noutaakseen tai tallettaakseen tietoa. Palvelinpuolen ohjelmointikielissä on huomattavasti enemmän vaihtoehtoja, sillä palvelin kommunikoi asiakkaan kanssa käyttämällä HTTP-protokollaa, johon löytyy kirjastoja lähes jokaiselle ohjelmointikielelle.
Palvelinpuolella tarkoitetaan ohjelman osaa, johon asiakaspuoli yhdistyy noutaakseen tai tallettaakseen tietoa. Palvelinpuolen ohjelmointikielissä on huomattavasti enemmän vaihtoehtoja, sillä palvelin kommunikoi asiakkaan kanssa käyttämällä HTTP-protokollaa, johon löytyy kirjastoja lähes jokaiselle ohjelmointikielelle.
\subsection{REST}
Palvelinpuoleen viitataan usein myös rajapintana (eng. Application Programming Interface, API), sillä tämänkaltaisia HTTP-protokollalla ohjattavia ohjelmia voi käyttää myös muuallakin kuin asiakaspuolen web-ohjelmissa. Tämänkaltaiset rajapinnat voivat myös yhdistyä toisiinsa internetin välityksellä ja näin jakaa tietoja eri palveluiden välillä.
Palvelinpuoleen viitataan usein myös rajapintana (eng. Application Programming Interface, API), sillä tämänkaltaisia HTTP-protokollalla ohjattavia ohjelmia voi käyttää myös muuallakin kuin asiakaspuolen web-ohjelmissa. Rajapinnat voivat myös yhdistyä toisiinsa internetin välityksellä ja näin jakaa tietoja eri palveluiden välillä.
Web-rajapintojen yleistyessä on kehitetty standardeja, minkä mukaan rajapintoja voi rakentaa. Näin rajapinnoista tulee helpompia käyttää kaikille. Yksi näistä standardeista on Representational state transfer (REST) \cite{wiki:rest}, jota pyrin käyttämään tämän opinnäytetyön projektia ohjelmoidessa.
Rajapintojen yleistyessä on kehitetty standardeja, joiden mukaan rajapintoja voi rakentaa. Näin rajapinnoista tulee helpompia käyttää kaikille. Yksi näistä standardeista on Representational state transfer (REST) \cite{wiki:rest}, jota pyrin käyttämään tämän insinöörityön projektia ohjelmoidessa.
\clearpage

View file

@ -5,21 +5,23 @@ Rust \cite{rust:lang} on Mozillan 2010 julkaisema ohjelmointikieli. Se on hyvin
\subsection{Muistinhallinta}
Monissa korkean tason ohjelmointikielissä, esimerkiksi JavaScriptissä, on automaattinen roskienkeräys \cite{wiki:garbagecollection} (engl. garbage collector). Se on prosessi, joka siivoaa muistista käyttämättömiä tietoja ja näin ollen vapauttaa muistia. Automaattisen roskienkeräyksen ongelma on, että se itsessään käyttää järjestelmän resursseja, ja roskienkeruu on hidasta.
Monissa korkean tason ohjelmointikielissä, esimerkiksi JavaScriptissä, on automaattinen roskienkeräys \cite{wiki:garbagecollection} (engl. garbage collector). Se on prosessi, joka siivoaa muistista käyttämättömiä tietoja ja näin ollen vapauttaa muistia. Automaattisen roskienkeräyksen ongelma on, että se itsessään käyttää järjestelmän resursseja. Lisäksi roskienkeruu on hidasta.
Automaattiselle roskienkeruulle on aikaisemmin ollut vaihtoehtona vain manuaalinen muistinhallinta, missä ohjelmoija varaa ja vapauttaa muistia tarpeen mukaan. Tämä on taas verratuna automaattiseen roskien keruuseen melko työlästä ja virheherkkää.
Automaattiselle roskienkeruulle on aikaisemmin ollut vaihtoehtona vain manuaalinen muistinhallinta, missä ohjelmoija varaa ja vapauttaa muistia tarpeen mukaan. Tämä on taas verrattuna automaattiseen roskien keruuseen melko työlästä ja virheherkkää.
Rustin yhtenä pääominaisuutena on mainostettu sen uudenlaista näkökulmaa muistinhallintaan: omistajuutta \cite{rust:ownership}. Siinä jokaisella arvolla on omistaja, ja kun omistaja menee näkyvyysalueen ulkopuolelle, niin menevät myös sen omistamat arvotkin, eli ne vapautetaan muistista. Arvojen omistajuutta voi siirtää joko pysyvästi tai väliaikaisesti lainaamalla.
\clearpage
\begin{code}
\inputminted{Rust}{code/ownership.rs}
\captionof{listing}{Omistajuus Rustissa}
\label{code:rust:ownership}
\end{code}
Koodiesimerkin \ref{code:rust:ownership} rivillä 9 tapahtuva ''name''-arvon tulostus ei toimi, koska arvon omistajuus on siirretty funktiolle ''say\char`_hello''. Funktion suorittamisen jälkeen se poistuu näkyvyysalueelta ja kaikki sen omistamat arvot vapautetaan muistista. Näin ollen arvoa ''name'' ei ole enää olemassa, kun sitä yritetään käyttää rivillä 9. Koodi saadaan toimimaan pienellä muutoksella:
Koodiesimerkin \ref{code:rust:ownership} rivillä 9 tapahtuva ''name''-arvon tulostus ei toimi, koska arvon omistajuus on siirretty funktiolle ''say\char`_hello''. Funktion suorittamisen jälkeen se poistuu näkyvyysalueelta ja kaikki sen omistamat arvot vapautetaan muistista. Näin ollen arvoa ''name'' ei ole enää olemassa, kun sitä yritetään käyttää rivillä 9. Koodi saadaan toimimaan pienellä muutoksella.
\clearpage
\bigskip
\begin{code}
\inputminted{Rust}{code/borrow.rs}
@ -30,7 +32,9 @@ Koodiesimerkin \ref{code:rust:ownership} rivillä 9 tapahtuva ''name''-arvon tul
Koodiesimerkissä \ref{code:rust:borrow} omistajuuden siirtäminen on korvattu lainauksella. Tämä muutos pitää tehdä sekä funktion parametrien määritykseen riville 1, että funktion kutsuun riville 7. Lainauksessa arvon omistajuus säilyy nykyisellään ja lainaaja antaa itse arvon sijasta viitteen (eng. reference). Viite on osoitin, joka osoittaa samaan muistipaikkaan kuin missä alkuperäinen arvo on. Lainaaminen tehdään käyttämällä merkkiä ''\char`&''.
\subsection{Vahva tyypitys}
Rust on vahvasti tyypitetty kieli, mikä tarkoittaa sitä, että kaikkien arvojen tyypit pitää olla tiedossa ohjelman kääntämisen aikana. Tähän sisältyy myös funktioiden parametrit ja paluuarvot. Usein Rustin kääntäjä osaa päätellä (eng. inference) arvojen tyypit itse, varsinkin yksinkertaisissa tapauksissa:
Rust on vahvasti tyypitetty kieli, mikä tarkoittaa sitä, että kaikkien arvojen tyypit pitää olla tiedossa ohjelman kääntämisen aikana. Tähän sisältyy myös funktioiden parametrit ja paluuarvot. Usein Rustin kääntäjä osaa päätellä (eng. inference) arvojen tyypit itse, varsinkin yksinkertaisissa tapauksissa.
\bigskip
\begin{code}
\inputminted{Rust}{code/type-inference.rs}
@ -38,16 +42,19 @@ Rust on vahvasti tyypitetty kieli, mikä tarkoittaa sitä, että kaikkien arvoje
\label{code:rust:inference}
\end{code}
Niissä tapauksissa, joissa tyypille voi olla useita vaihtoehtoja, tai silloin jos arvon määrityksen yhteydessä tapahtuu konversio, ohjelmoijan tulee määrittää tyyppi. Tyypin voi määrittää näin:
Niissä tapauksissa, joissa tyypille voi olla useita vaihtoehtoja, tai silloin jos arvon määrityksen yhteydessä tapahtuu konversio, ohjelmoijan tulee määrittää tyyppi.
\bigskip
\begin{code}
\inputminted{Rust}{code/type-notation.rs}
\captionof{listing}{Tyypin merkintä}
\label{code:rust:type-notation}
\end{code}
\clearpage
Koodiesimerkissä \ref{code:rust:type-notation} valitsin arvolle ''age'' tyypin u8, koska se on pienin kokonaislukutyypeistä ja sen arvo voi olla välillä 0-255. Näin voi potentiaalisesti vähentää ohjelman muistin käyttöä. Lisäksi tyypeillä voi myös karkeasti rajata funktion parametrien arvojen vaihteluväliä. Esimerkiksi jos on kirjoittamassa funktiota, joka ottaa parametrina henkilön iän, u8 on hyvä valinta, koska ihmisen ikä ei voi olla negatiivinen ja yksikään ihminen tuskin elää yli 255 vuotta.
Valitsin arvolle ''age'' tyypin u8, koska se on pienin kokonaislukutyypeistä ja sen arvo voi olla välillä 0-255. Näin voi potentiaalisesti vähentää ohjelman muistin käyttöä. Lisäksi tyypeillä voi myös karkeasti rajata funktion parametrejen arvojen vaihteluväliä. Esimerkiksi jos on kirjoittamassa funktiota joka ottaa parametrina henkilön iän, u8 on hyvä valinta koska ihmisen ikä ei voi olla negatiivinen ja yksikään ihminen tuskin elää yli 255 vuotta.
\clearpage
Edellä mainittu tyyppi u8 on niin kutsuttu etumerkitön kokonaisluku (eng. \textbf{u}nsigned integer). Etumerkillisissä kokonaisluvuissa (eng. signed integer) käytetään yksi bitti merkkaamaan sitä, onko luku positiivinen vai negatiivinen. Etumerkittömissä luvuissa tätä ei tehdä, joten luku voi olla hieman isompi kuin etumerkillinen luku, mutta se ei voi olla negatiivinen.
@ -92,7 +99,9 @@ Edellä mainittu tyyppi u8 on niin kutsuttu etumerkitön kokonaisluku (eng. \tex
\bigskip
Toisin kuin C- ja C++ -kielissä, Rustissa on oletuksena etumerkittömien kokonaislukujen ylivuoto pois päältä. Tämä tarkoittaa sitä, että jos esimerkiksi 8-bittisen etumerkittömän kokonaisluvun (u8) arvo on 256, siitä tulee 0. Rustin kääntäjä siis ei anna tällaisen tapahtua vaan kääntämisen yhteydessä tulee virheviesti, jonka voi nähdä koodiesimerkissä \ref{code:rust:integer-overflow}.
Toisin kuin C- ja C++ -kielissä, Rustissa on oletuksena etumerkittömien kokonaislukujen ylivuoto pois päältä. Tämä tarkoittaa sitä, että jos esimerkiksi 8-bittisen etumerkittömän kokonaisluvun (u8) arvoksi yritetään asettaa 256, siitä tulee 0. Rustin kääntäjä siis ei anna tällaisen tapahtua vaan kääntämisen yhteydessä tulee virheviesti, jonka voi nähdä koodiesimerkissä \ref{code:rust:integer-overflow}.
\bigskip
\begin{code}
\inputminted[linenos=false]{shell}{code/integer-overflow}
@ -100,16 +109,18 @@ Toisin kuin C- ja C++ -kielissä, Rustissa on oletuksena etumerkittömien kokona
\label{code:rust:integer-overflow}
\end{code}
\clearpage
\subsection{Muuttumaton data}
Rustissa kaikki arvot ovat oletuksena muuttumattomia (engl.\ immutable). Jos muuttumattoman datan sijasta tarvitsee muuttujia (engl. mutable), voi käyttää avainsanaa ''mut'', esimerkiksi \mintinline{Rust}{let mut name = "Marko"}. Myös lainaukset suoritetaan oletuksena muuttumattomasti ja muutettavan lainauksen voi tehdä samalla avainsanalla, esimerkiksi \mintinline{Rust}{say_hello(&mut name)}
Yleinen konsensus ohjelmoinnin maailmassa on se, että kaikki arvot, joita ei tarvitse muuttaa, pitäisi nimenomaan määrittää muuttumattomana. Tämä on tuttua kaikille, jotka ovat tutustuneet funktionaalisiin ohjelmointikieliin. Muuttumaton data on myös todella tärkeää rinnakkaisajossa, missä useampi prosessi suorittaa samoja funktioita ja käsittelee samoja arvoja samaan aikaan.
Yleinen konsensus ohjelmoinnin maailmassa on se, että kaikki arvot, joita ei tarvitse muuttaa, pitäisi nimenomaan määrittää muuttumattomina. Tämä on tuttua kaikille, jotka ovat tutustuneet funktionaalisiin ohjelmointikieliin. Muuttumaton data on myös todella tärkeää rinnakkaisajossa, missä useampi prosessi suorittaa samoja funktioita ja käsittelee samoja arvoja samaan aikaan.
\subsection{Luotettavuus}
\label{sect:luotettavuus}
Rustia kehitettäessä on aina ollut tavoitteena luotettavuus. Tämä tarkoittaa sitä, että ohjelman virheet huomataan jo kääntämisen yhteydessä, eikä vasta suorituksen aikana ohjelman tietyssä tilassa. Tämän mahdollistavat edellä mainitut omistajuusmalli ja vahva tyypitys. Omistajuusmalli varmistaa sen, että ohjelmoija joutuu koodia kirjoittaessaan miettimään arvojen eliniän, joka tekee muistivuodoista harvinaisia. Vahva tyypitys varmistaa taas sen, että data on kaikkialla ohjelmassa yhteensopivaa.
Myös Rustin kääntäjään on panostettu paljon, ja virheiden sattuessa se on todella hyvä työkalu ohjelmoijalle. Se alleviivaa ongelmakohdat ja selittää lyhyesti, mistä ongelma johtuu. Jossain tapauksissa kääntäjä jopa antaa pieniä koodin pätkiä, mistä voi olla apua ongelman ratkaisemisessa:
Myös Rustin kääntäjään on panostettu paljon, ja virheiden sattuessa se on todella hyvä työkalu ohjelmoijalle. Se alleviivaa ongelmakohdat ja selittää lyhyesti, mistä ongelma johtuu. Jossain tapauksissa kääntäjä jopa antaa pieniä koodin pätkiä, mistä voi olla apua ongelman ratkaisemisessa [koodiesimerkki \ref{code:rust:help}].
\clearpage
@ -120,14 +131,14 @@ Myös Rustin kääntäjään on panostettu paljon, ja virheiden sattuessa se on
\end{code}
\subsection{Suorituskyky}
Monista luotettavuuteen liittyvistä ominaisuuksista johtuen voisi luulla että kaikki nämä ominaisuudet vaikuttaisivat negatiivisesti suorituskykyyn. Monissa kielissä onkin runtime-kirjasto \cite{wiki:runtime-library}. Se on kääntäjän jatke, joka lisää käännettyyn binääritiedostoon ajon aikana suoritettavia käskyjä, jotka liittyvät juuri tällaisten luotettavuusominaisuuksien takaamiseen. Nämä käskyt suoritetaan siis itse ohjelman suorituksen yhteydessä, joten ne käyttävät prosessoriaikaa ja muistia aina kun ohjelma on käynnissä.
Rustin monista luotettavuuteen liittyvistä ominaisuuksista johtuen voisi luulla, että kaikki nämä ominaisuudet vaikuttaisivat negatiivisesti suorituskykyyn. Monissa kielissä onkin runtime-kirjasto \cite{wiki:runtime-library}. Se on kääntäjän jatke, joka lisää käännettyyn binääritiedostoon ajon aikana suoritettavia käskyjä, jotka liittyvät juuri tällaisten luotettavuusominaisuuksien takaamiseen. Nämä käskyt suoritetaan siis itse ohjelman suorituksen yhteydessä, joten ne käyttävät prosessoriaikaa ja muistia aina kun ohjelma on käynnissä.
Rustissa ei ole runtime-kirjastoa. Kääntäjä tekee kaikki runtime-kirjaston tehtävät kääntämisen yhteydessä. Jos jokin tarkistus ei mene läpi, esimerkiksi omistajuusmallia on käytetty väärin, ohjelman kääntäminen keskeytetään ja kehittäjälle näytetään virheviesti.
Runtime-kirjaston puuttuminen vapauttaa prosessointiaikaa ja muistia itse ohjelman käyttöön, mikä parantaa suorituskykyä.
\subsection{Turvallinen rinnakkaisajo}
Rinnakkaisajo tarkoittaa sitä, että jotakin ohjelman osaa suoritetaan saman aikaan useassa prosessorin säikeessä. Rinnakkaisajolle sopivissa tehtävissä kuorman jakamisella useammalle säikeelle saavutetaan parempaa suorituskykyä.
Rinnakkaisajo tarkoittaa sitä, että jotakin ohjelman osaa suoritetaan saman aikaan useassa prosessorin säikeessä. Rinnakkaisajolle sopivissa tehtävissä kuorman jakamisella saavutetaan parempaa suorituskykyä.
Turvallisella rinnakkaisajolla \cite{wiki:thread-safety} tarkoitetaan sitä, että jos rinnakkaisajossa käsitellään tietorakenteita, käsittely suoritetaan turvallisesti. Turvallisuus tarkoittaa sitä, että saavutetaan haluttu tulos eikä ilmene sivuvaikutuksia.
@ -135,7 +146,9 @@ Rust takaa turvallisen rinnakkaisajon luvussa \ref{sect:luotettavuus} mainituill
\subsection{Makrot}
\label{chap:macros}
Yksi todella mielenkiintoinen ominaisuus Rustissa on makrot. Se on toiminnallisuus mikä mahdollistaa metaohjelmoinnin \cite{wiki:metaprogramming}. Metaohjelmoinnissa koodia voi generoida kääntämisen aikana, mikä on erityisen hyödyllistä esimerkiksi silloin, kun ohjelmoija tarvitsee useita toiminnalisuudeltaan samankaltaisia funktioita.
Yksi todella mielenkiintoinen ominaisuus Rustissa on makrot. Se on toiminnallisuus mikä mahdollistaa metaohjelmoinnin \cite{wiki:metaprogramming}. Metaohjelmoinnissa koodia voi generoida kääntämisen aikana, mikä on erityisen hyödyllistä esimerkiksi silloin, kun ohjelmoija tarvitsee useita toiminnallisuudeltaan samankaltaisia funktioita.
\bigskip
\begin{code}
\inputminted{Rust}{code/macro.rs}
@ -145,7 +158,9 @@ Yksi todella mielenkiintoinen ominaisuus Rustissa on makrot. Se on toiminnallisu
\clearpage
Makron sisällä suluissa olevat lauseet ovat verrattavissa Rustin \mintinline{Rust}{match} -lauseeseen. Kun makron syöte vastaa jotakin näistä lauseista, hakasulkujen sisällä oleva koodi generoidaan. Makrossa käytetty \mintinline{Rust}{println!()} on myös itsessään makro, joka tulee Rustin ''std'' -kirjaston mukana.
Makron sisällä suluissa olevat lauseet ovat verrattavissa Rustin ''match''-lauseeseen. Kun makron syöte vastaa jotakin näistä lauseista, hakasulkujen sisällä oleva koodi generoidaan. Makrossa käytetty ''println!()'' on myös itsessään makro, joka tulee Rustin ''std'' -kirjaston mukana.
\bigskip
\begin{code}
\inputminted{Rust}{code/println.rs}
@ -155,9 +170,16 @@ Makron sisällä suluissa olevat lauseet ovat verrattavissa Rustin \mintinline{R
Metaohjelmointi avaa aivan uudenlaisia mahdollisuuksia sille, mitä ohjelmointikielellä voi tehdä. Makroilla voi toteuttaa vaikka kokonaisen ohjelmointikielen \cite{rust:macro-lisp}.
\subsection{Dokumentaatio ja yhteisö}
Rust on tunnettu todella laajasta dokumentaatiostaan ja vahvasta yhteisöstään. Molemmista on paljon apua varsinkin aloittelijoille.
Aloitin itsekin opiskelemaan Rustia vain hieman ennen tämän insinöörityön alkua ja yhteisöstä oli monesti apua projektin aikana vastaan tulleissa ongelmissa.
\subsection{Paketinhallinta}
\label{sect:paketinhallinta}
Rustin paketinhallinta on toteutettu Cargo-nimisellä ohjelmalla. Sitä voi käyttää koko ohjelmiston elinkaaren ajan aina projektin luomisesta sen julkaisemiseen. Cargon käsittelemiä paketteja kutsutaan laatikoiksi (eng. crate), jotka julkaistaan crates.io \cite{rust:cratesio} pakettirekisterissä. Laatikot voivat myös olla riippuvaisia toisista laatikoista. Laatikon tiedot ja riippuvuudet määritetään Cargo.toml-tiedostossa.
Rustin paketinhallinta on toteutettu Cargo-nimisellä ohjelmalla. Sitä voi käyttää koko ohjelmiston elinkaaren ajan aina projektin luomisesta sen julkaisemiseen. Cargon käsittelemiä paketteja kutsutaan laatikoiksi (eng. crate), jotka julkaistaan crates.io \cite{rust:cratesio} pakettirekisterissä. Laatikot voivat myös olla riippuvaisia toisista laatikoista. Laatikon tiedot ja riippuvuudet määritetään Cargo.toml-tiedostossa [koodiesimerkki \ref{code:rust:cargo-toml}].
\clearpage
\begin{code}
\inputminted{TOML}{code/Cargo.toml}
@ -167,29 +189,29 @@ Rustin paketinhallinta on toteutettu Cargo-nimisellä ohjelmalla. Sitä voi käy
Cargoon on saatavilla myös useita liitännäisiä, esimerkiksi cargo-watch, joka suorittaa halutun toiminnon aina, kun projektin sisällä tapahtuu muutoksia sekä tässäkin projektissa käytetty cargo-web, joka helpottaa WebAssembly-ohjelmien kehittämistä.
\subsection{Dokumentaatio ja yhteisö}
Rust on tunnettu todella laajasta dokumentaatiostaan ja vahvasta yhteisöstään. Molemmista on paljon apua varsinkin aloittejioille.
Aloitin itsekin opiskelemaan Rustia vain hieman ennen tämän opinnäytetyön alkua ja yhteisöstä oli monesti apua projektin aikana vastaan tulleissa ongelmissa.
\section{WebAssembly}
WebAssembly \cite{webassembly:home} on kehitteillä oleva asiakaspuolen ohjelmointikieli. Sitä on suunniteltu JavaScriptin seuraajaksi ja sen suurimpana etuna JavaScriptiin verrattuna on huomattavasti matalamman tason esitysmuoto, minkä ansiosta se on suorituskykyisempi.
Kehittäjän ei ole tarkoitus kirjoittaa WebAssemblya itse, vaan käyttää työkaluja, joilla olemassa olevia ohjelmointikieliä voi kääntää WebAssemblyksi. Rust on tästä hyvä esimerkki, sillä WebAssembly on yksi sen kääntäjän natiiveista ''targeteista'', samalla tavalla kuin vaikka x86-prosessorit.
WebAssembly on siis ensisijaisesti binääriformaatti, mutta sen voi muuntaa myös tekstiformaatiksi, jonka nimi on WebAssembly text \cite{webassembly:text}. WebAssembly text käyttää syntaksissaan S-lausekkeita \cite{s-expression}. Se on notaatio puurakenteiselle datalle, joka on kehitetty Lisp-ohjelmointikieltä varten, joten WebAssembly text muistuttaa syntaksiltaan hyvin paljon Lispiä. WebAssembly textiä käytetään tilanteissa, joissa ihmisen täytyy ymmärtää mitä koodissa tapahtuu, esimerkiksi WebAssemblyn sisäisessä kehityksessä ja web-ohjelmistojen debuggereissa.
\clearpage
WebAssembly on siis ensisijaisesti binääriformaatti, mutta sen voi muuntaa myös tekstiformaatiksi, jonka nimi on WebAssembly text \cite{webassembly:text}. WebAssembly text käyttää syntaksissaan S-lausekkeita \cite{s-expression}. Se on notaatio puurakenteiselle datalle, joka on kehitetty Lisp-ohjelmointikieltä varten, joten WebAssembly text muistuttaa syntaksiltaan hyvin paljon Lispiä. WebAssembly textiä käytetään tilanteissa, joissa ihmisen täytyy ymmärtää, mitä koodissa tapahtuu. Tätä hyödynnetään esimerkiksi WebAssemblyn sisäisessä kehityksessä ja web-ohjelmistojen debuggereissa.
\bigskip
\begin{code}
\inputminted{Lisp}{code/plus.wat}
\captionof{listing}{Lukujen yhteenlaskufunktio WebAssembly text -formaatissa}
\label{code:webassembly:plus}
\end{code}
\vspace{\baselineskip}
\bigskip
\begin{code}
\inputminted{Rust}{code/plus.rs}
\captionof{listing}{Lukujen yhteenlaskufunktio Rustilla}
\label{code:rust:plus}
\end{code}
Koodiesimerkeistä \ref{code:webassembly:plus} ja \ref{code:rust:plus} voi nähdä saman koodin sekä Rustilla että WebAssembly text -formaatissa. Esimerkeistä huomaa hyvin WebAssemblyn matalemman esitysmuodon käytännössä.

View file

@ -1,12 +1,14 @@
\chapter{Projekti}
Tein opinnäytetyön yhteydessä fullstack-projektin, jossa sekä palvelin- että asiakaspuolen ohjelmointi tehtiin Rustilla. Tarkoituksena ei ollut saada aikaiseksi mitään todella monimutkaista ohjelmaa, vaan puhtaasti arvioida Rustin soveltuvuutta web-ohjelmointiin.
Tein insinöörityön yhteydessä full-stack projektin, jossa sekä palvelin- että asiakaspuolen ohjelmointi tehtiin Rustilla. Tarkoituksena ei ollut saada aikaiseksi mitään todella monimutkaista ohjelmaa, vaan puhtaasti arvioida Rustin soveltuvuutta web-ohjelmointiin.
\section{Kehitysympäristön asennus}
Rust-projektin aloittamiseksi kehittäjä tarvitsee koneelleen Rustin paketinhallintatyökalun, Cargon (katso luku \ref{sect:paketinhallinta}). Olen itse Linux-käyttäjä, joten sain asennettua Cargon Linux-jakeluni ohjelmavarastosta. Windows-käyttäjille suosittelen käyttämään Rustup-asennusohjelmaa. Lisää tietoa Rustin asentamisesta saa Rustin kotisivuilta \cite{rust:install}.
\clearpage
Projektin saa initialisoitua komennolla \mintinline{shell}{cargo init projektinnimi}. Tämä komento luo hakemiston ''projektinnimi'', minkä sisällä on Cargon konfiguraatiotiedosto Cargo.toml, jossa voi määrittää projektin tiedot ja riippuvuudet.
Lähdekoodi sijaitsee hakemistossa ''src''. Cargo kirjoittaa hakemistoon valmiiksi ''main.rs'' -tiedoston, jossa on ''Hello world!'' esimerkkikoodi. ''main.rs'' on aina Rust-ohjelman ensimmäiseksi suoritettava tiedosto, eli niin kutsuttu entrypoint. ''main.rs'' tiedoston sisällä pitää olla ''main()''-funktio, josta ohjelman suoritus alkaa. Projektin voi suorittaa komennolla \mintinline{shell}{cargo run}.
Lähdekoodi sijaitsee hakemistossa ''src''. Cargo kirjoittaa hakemistoon valmiiksi ''main.rs'' -tiedoston, jossa on ''Hello world!'' esimerkkikoodi. ''main.rs'' on aina Rust-ohjelman ensimmäiseksi suoritettava tiedosto, eli niin kutsuttu entrypoint. ''main.rs''-tiedoston sisällä pitää olla ''main()''-funktio, josta ohjelman suoritus alkaa. Projektin voi suorittaa komennolla \mintinline{shell}{cargo run}.
Suosittelen asentamaan myös muutaman Cargon liitännäisen helpottamaan kehitystä. cargo-watchin avulla voi ajaa komennon aina, kun lähdekoodi muuttuu. Esimerkiksi komennolla \mintinline{shell}{cargo watch -x run} voi kääntää ja käynnistää projektin aina uudelleen, kun lähdekoodi muuttuu. cargo-add -liitännäisellä voi helposti lisätä uusia riippuvuuksia projektiinsa. Esimerkiksi Actix webin saa lisättyä komennolla \mintinline{shell}{cargo add actix-web}.
@ -16,11 +18,13 @@ Näillä ohjeilla pääsee alkuun palvelinpuolen kehityksessä. Asiakaspuolen ke
\subsection{Kehys}
Palvelinpuolen kehykseksi valikoitui Actix web. Se on käytännössä vastine JavaScript-maailman Express.js:lle, eli se hoitaa HTTP-palvelimen työtä ja reitittää GET- ja POST-pyynnöt ohjelman oikeille funktioille.
Actix web on puolestaan rakennettu hyödyntämällä Actix-sovelluskehystä, mikä on rakennettu löyhästi actor-mallin pohjalta. Actor-malli \cite{wiki:actor} on Carl Hewittin vuonna 1973 luoma matemaattinen ja tietotekninen malli rinnakkaisajosta. Tämän ansiosta Actix web on hyvin suorituskykyinen ja helposti skaalautuva ratkaisu rajapintoja rakennettaessa.
Actix web on puolestaan rakennettu hyödyntämällä Actix-sovelluskehystä, joka on rakennettu löyhästi actor-mallin pohjalta. Actor-malli \cite{wiki:actor} on Carl Hewittin vuonna 1973 luoma matemaattinen ja tietotekninen malli rinnakkaisajosta. Tämän ansiosta Actix web on hyvin suorituskykyinen ja helposti skaalautuva ratkaisu rajapintoja rakennettaessa.
\clearpage
Actix web on myös hyvin integroitu Rustin vahvaan tyypitykseen. Erityisen vaikuttavaa oli se, että esimerkiksi tietyn rajapinnan päätepisteen POST-pyyntöön pystyy määrittämään parametriksi tietyn rakenteen (eng. structure) [koodiesimerkki \ref{code:rust:registeruser}].
\clearpage
\bigskip
\begin{code}
\inputminted{Rust}{code/registeruser.rs}
@ -36,15 +40,19 @@ Tällöin varmistutaan automaattisesti siitä, että kun asiakkaan POST-pyyntö
\label{code:json:registeruser}
\end{code}
Kun koodiesimerkin \ref{code:rust:registeruser} funktioon ''register'' saapuu koodiesimerkin \ref{code:json:registeruser} mukainen JSON objekti, Actix huomaa väärän tyypin ja vastaa statuskoodilla ''400 Bad Request'', koska objektin parametri \mintinline{JSON}{"admin"} ei ole tyyppiä \mintinline{Rust}{bool}.
Kun koodiesimerkin \ref{code:rust:registeruser} funktioon ''register'' saapuu koodiesimerkin \ref{code:json:registeruser} mukainen JSON-objekti, Actix huomaa väärän tyypin ja vastaa statuskoodilla ''400 Bad Request'', koska objektin parametri \mintinline{JSON}{"admin"} ei ole tyyppiä \mintinline{Rust}{bool}.
\subsection{Todentaminen}
Actix webin modulaarisuus mahdollisti myös helposti tarvittavien väliohjelmistojen sisällyttämisen ohjelman toimintaan. Actix identity -paketista löytyi tarvittavat palikat joilla sain lisättyä itse kirjoittamani käyttäjän todentamistoiminnallisuuden suojaamaan haluttuja reittejä.
Palvelinpuolen initialisointi ja käynnistäminen löytyvät liitteestä \ref{appx:actix}.
\clearpage
Todentamiseen päätin käyttää JSON Web Tokeneita. Ne ovat standardoitu (RFC 7519\cite{jwt:home}) tunnistautumistapa verkossa. Tokenit ovat merkkijonoja, jotka sisältävät JavaScript-objektin tekstimuodossa (JSON).
\subsection{Todentaminen}
Actix webin modulaarisuus mahdollisti myös helposti tarvittavien väliohjelmistojen sisällyttämisen ohjelman toimintaan. Actix identity -paketista löytyi tarvittavat palikat, joilla sain lisättyä itse kirjoittamani käyttäjän todentamistoiminnallisuuden suojaamaan haluttuja reittejä.
Todentamiseen päätin käyttää JSON Web Tokeneita. Ne ovat standardoitu (RFC 7519\cite{jwt:home}) tunnistautumistapa verkossa. Tokenit ovat merkkijonoja, jotka sisältävät JavaScript-objektin tekstimuodossa (JSON). Yhden tällaisen tokenin sisällön voi nähdä koodiesimerkissä \ref{code:json:jwt}.
\bigskip
\begin{code}
\inputminted{Rust}{code/jwt.json}
@ -54,18 +62,22 @@ Todentamiseen päätin käyttää JSON Web Tokeneita. Ne ovat standardoitu (RFC
Tämä tokenin sisältö on kaikkien sen hallussapitäjien nähtävissä. Tokenin turvallisuus tulee siitä, että token on allekirjoitettu palvelinpuolella vain palvelimen tiedossa olevalla salasanalla niin, että jos tokenin sisältö muuttuu yhtään, palvelin näkee että se ei ole enää validi.
Otetaan esimerkkinä tämän sovelluksen käyttäjä Pasi. Pasi ei ole ylläpitäjä, mutta hän koittaa tehdä itsestään ylläpitäjän muuttamalla omasta tokenistaan parametrin \mintinline{JavaScript}{"admin": false} arvoksi \mintinline{JavaScript}{true}. Kun Pasi lähettää tämän muokatun tokenin palvelimelle, missä se viedään JWT-validointifunktioon. Palvelin huomaa että token ei ole enää validi, koska sen sisältöä ei ole allekirjoitettu palvelimen salasanalla.
Otetaan esimerkkinä tämän sovelluksen käyttäjä Pasi. Pasi ei ole ylläpitäjä, mutta hän koittaa tehdä itsestään ylläpitäjän muuttamalla omasta tokenistaan parametrin \mintinline{JavaScript}{"admin": false} arvoksi \mintinline{JavaScript}{true}. Kun Pasi lähettää tämän muokatun tokenin palvelimelle, se viedään JWT-validointifunktioon. Palvelin huomaa, että token ei ole enää validi, koska sen sisältöä ei ole allekirjoitettu palvelimen salasanalla.
Tokenin sisällön voi päättää kokonaan itse, vaikkakin joitakin standardeja kenttiä on määritetty, esimerkiksi iss (issuer), sub (subject) ja exp (expiration time). Päätin sisällyttää tiedon siitä, että onko käyttäjä ylläpitäjä, koska tätä tietoa voi sitten käyttää asiakaspuolella esimerkiksi käyttöliittymän muokkaamiseen käyttäjän roolin perusteella. Usein myös käyttäjän nimi sisällytetään tokeniin. Tokenin sisältöä suunnitellessa kannattaa pitää mielessä, että sen sisältö on nähtävissä kaikille, joten se ei ole oikea paikka tallettaa salaista tietoa, kuten vaikka käyttäjän salasana.
Tokenin sisällön voi päättää kokonaan itse, vaikkakin joitakin standardeja kenttiä on määritetty. Esimerkiksi iss (issuer), sub (subject) ja exp (expiration time). Päätin sisällyttää myös tiedon siitä, että onko käyttäjä ylläpitäjä, koska tätä tietoa voi sitten käyttää asiakaspuolella esimerkiksi käyttöliittymän muokkaamiseen käyttäjän roolin perusteella. Usein myös käyttäjän nimi sisällytetään tokeniin. Tokenin sisältöä suunnitellessa kannattaa pitää mielessä, että sen sisältö on nähtävissä kaikille, joten se ei ole oikea paikka tallettaa salaista tietoa, kuten vaikka käyttäjän salasana.
Päätin tallettaa edellä mainitun JSON Web Tokenin evästeeseen (eng. cookie), joka on standardi tapa tallettaa juuri tällaisia todentamiseen käytettäviä tietoja selaimiin. Evästeiden käyttämisen etu on se, että selain huolehtii sen tallettamisesta automaattisesti ilman lisätoimia kehittäjältä. Lisäksi selain sisällyttää sen seuraaviin kutsuihin automaattisesti.
Projektin koko JWT-toteutus on liitteessä \ref{appx:jwt}.
\subsection{CORS}
Lisäsin palvelimelle myös Cross-Origin Resource Sharing (CORS) \cite{wiki:cors} -toiminnallisuuden. Oletuksena selaimen lataama ohjelma saa ladata resursseja vain samasta osoitteesta, kuin mistä itse ohjelma on ladattu. Tämä on turvallisuuskäytäntö, joka kulkee nimellä Same-origin policy (SOP) \cite{wiki:sop}. Näihin resursseihin sisältyvät muunmuassa CSS-tyylimääritykset, kuvat ja JavaScript-ohjelmat.
Lähes kaikki nykyiset web-ohjelmistot kuitenkin vaativat näiden ulkoisien resurssien käyttöä. CORS on tekniikka, jossa palvelin kertoo selaimelle sallitut osoitteet, mistä resursseja saa noutaa. CORS:n lisääminen tähän projektiin hoitui Actixin liitännäisellä actix\char`_cors.
CORS:n asetusten määritys löytyy liitteestä \ref{appx:actix}.
\subsection{Tietokanta}
\label{project:database}
@ -75,14 +87,16 @@ Käytännöllisimmät ominaisuudet kehittäjän näkökulmasta olivat Dieselin m
Edellä mainitut työkalut helpottivat tietokannan kehitystä huomattavasti. Usein varsinkin projektin alkuvaiheilla tietokanta muuttuu jatkuvasti ja usein tulee tarve poistaa ja luoda tietokanta uudelleen. Monet kehittäjät pitävät juurikin tällaista Dieselin up.sql kaltaista tiedostoa versiohallinnassa ja tarpeen mukaan poistavat tietokannan käsin ja liittävät komennon tiedostosta tietokannan komentorivikäyttöliittymään. Dieseliä käytettäessä tämä tulee tehtyä automaattisesti ja se tuntuu todella luontevalta.
Päätin myös käyttää Dieselin kanssa yhteyksien yhdistämistä (eng. connection pooling). Tämä tarkoittaa sitä, että palvelin luo käynnistyessään prosessin, joka ottaa yhteyden tietokantaan, ja jakaa tätä yhteyttä sitä tarvitseville ohjelman osille. Tämä on tehokkaampaa, kuin vaihtoehto, jossa otetaan uusi yhteys jokaista tietokantakyselyä varten.
Päätin myös käyttää Dieselin kanssa yhteyksien yhdistämistä (eng. connection pooling). Tämä tarkoittaa sitä, että palvelin luo käynnistyessään prosessin, joka ottaa yhteyden tietokantaan, ja jakaa tätä yhteyttä sitä tarvitseville ohjelman osille. Tämä on tehokkaampaa kuin vaihtoehto, jossa otetaan uusi yhteys jokaista tietokantakyselyä varten.
\section{Asiakaspuoli}
\subsection{Asennus}
\label{sect:asiakaspuoli:asennus}
Projektin asiakaspuolen aloittaminen vaati aika paljon tutkimustyötä. Rust-koodi pitää kääntää WebAssemblyksi, johon on olemassa useita eri työkaluja. Lisäksi selaimien rajapintojen käyttämiseen tarvitsee jonkinlaisen kirjaston, joita Rust-maailmassa on tällä hetkellä kaksi: stdweb\cite{webassembly:stdweb} ja web\char`_sys\cite{webassembly:websys}. Näistä stdweb on vanhempi, mutta myös tuetumpi. web\char`_sys on uudempi tulokas ja näyttää siltä, että se tulee korvaamaan stdweb:n tulevaisuudessa.
Valitsemani sovelluskehys tukee molempia kirjastoja, mutta päädyin kuitenkin valitsemaan stdweb:n. Suurimpana syynä oli projektin aloittamisen helppous, joka tehdään Cargon liitännäisellä cargo-web. Toisena syynä oli, että WebAssembly-ohjelman paketointi web\char`_sys:n kanssa on tätä kirjoittaessani on riippuvainen NodeJS:stä, kun taas stdweb ei vaadi NodeJS:n asennusta kehittäjän koneelle ollenkaan.
Valitsemani sovelluskehys tukee molempia kirjastoja, mutta päädyin kuitenkin valitsemaan stdweb:n. Suurimpana syynä oli projektin aloittamisen helppous, joka tehdään Cargon liitännäisellä cargo-web. Toisena syynä oli, että WebAssembly-ohjelman paketointi web\char`_sys:n kanssa on tätä kirjoittaessani riippuvainen NodeJS:stä, kun taas stdweb ei vaadi NodeJS:n asennusta kehittäjän koneelle ollenkaan.
\clearpage
WebAssembly-koodin suorittamiseksi selain tarvitsee pienen pätkän JavaScriptiä, joka tekee tarvittavat toimet WebAssembly-ohjelman käynnistämiseksi. Tämän koodin generoi puolestani cargo-web. Lisäksi JavaScriptin suorittamiseen tarvitaan yksi HTML-tiedosto. Tähän tiedostoon voi myös sisällyttää metadataa, kuten esimerkiksi sivuston otsikon, joka näkyy selaimen välilehdessä. Tähän tiedostoon linkitetään myös kaikki muu staattinen data, kuten tyylimääritykset. Nämä tiedostot laitoin käytännön mukaisesti static-nimiseen hakemistoon asiakaspuolen projektin juureen. Tämän hakemiston linkitin symbolisella linkillä palvelinpuolen projektiin, jonka HTTP-serveri voi sitten lähettää HTML-dokumentin, JavaScript-tiedoston ja WebAssembly-binäärin käyttäjän selaimelle.
@ -92,7 +106,9 @@ Asiakaspuolen sovelluskehykseksi valitsin Yew:n\cite{yew:home}. Yew muistuttaa h
\subsection{Ulkonäkö}
\label{sect:ulkonäkö}
Olen JavaScript-maailmassa tottunut siihen, että käyttöliittymäkehyksiin löytyy usein kirjasto, joka tarjoaa valmiit tyylimääritykset, mutta en löytänyt vastaavaista kirjastoa, joka toimisi Yew:n kanssa. Kaikki käyttöliittymän elementit olivat siis selaimen oletustyylisiä. En halunnut ryhtyä tässä projektissa kirjoittamaan CSS-määrityksiä alusta alkaen, joten päädyin käyttämään UIkittiä\cite{uikit}. UIkit on CSS-kirjasto, missä on paljon valmiita ja hyvännäköisiä tyylimäärityksiä. UIkitin dokumentaatio on laaja ja siellä on paljon esimerkkejä, joten alkuun pääsi todella nopeasti (kuva \ref{fig:login}).
Olen JavaScript-maailmassa tottunut siihen, että käyttöliittymäkehyksiin löytyy usein kirjasto, joka tarjoaa valmiit tyylimääritykset, mutta en löytänyt vastaavaista kirjastoa, joka toimisi Yew:n kanssa. Kaikki käyttöliittymän elementit olivat siis selaimen oletustyylisiä. En halunnut ryhtyä tässä projektissa kirjoittamaan CSS-määrityksiä alusta alkaen, joten päädyin käyttämään UIkittiä\cite{uikit}. UIkit on CSS-kirjasto, missä on paljon valmiita ja hyvännäköisiä tyylimäärityksiä. UIkitin dokumentaatio on laaja ja siellä on paljon esimerkkejä, joten alkuun pääsi todella nopeasti [kuva \ref{fig:login}].
\clearpage
\begin{figure}[h]
\centering
@ -101,32 +117,37 @@ Olen JavaScript-maailmassa tottunut siihen, että käyttöliittymäkehyksiin lö
\label{fig:login}
\end{figure}
\subsection{Reititys}
Reititykseen käytin Yew:n liitännäistä, yew\char`_routeria. Reititys asiakaspuolen ohjelmassa tarkoittaa sitä, että selaimen osoitepalkissa olevan polun mukaan käyttäjä reititetään oikeaan ohjelman osaan. Tämä liittyy käsitykseen yhden sivun ohjelmista (katso luku \ref{sect:spa}), joissa selain suorittaa yhden ohjelman, jonka jälkeen perinteisiä sivujen latauksia ei enää tapahtu. Ohjelma muokkaa osoitepalkissa näkyvää osoitetta, jonka pohjalta sitten reititys oikeaan komponenttiin tapahtuu. Käyttöliittymää muokataan käyttämällä DOM:ia, joka mahdollistaa sivun sisällön muokkaamisen ilman sivun uudelleen lataamista.
Login-komponentti löytyy kokonaisuudessaan liitteestä \ref{appx:login}. Liitteestä näkee esimerkin Yew:n komponenttirajapinnan käytöstä ja palvelimeen yhdistämisestä. Lisäksi siellä on käytetty UIKit-tyylityksiä.
Asiakaspuolen reititys vaatii myös palvelinpuolelta sen, että kaikki mahdolliset asiakaspuolen reitit palauttavat ohjelman. Toinen edellytys on se, että palvelin ei palauta uudelleenohjausta (HTTP 302), koska silloin myös osoitepalkissa oleva osoite muuttuu eikä sitä voida välittää asiakaspuolen ohjelmalle.
\subsection{Reititys}
Reititykseen käytin Yew:n liitännäistä, yew\char`_routeria. Reititys asiakaspuolen ohjelmassa tarkoittaa sitä, että selaimen osoitepalkissa olevan polun mukaan käyttäjä reititetään oikeaan ohjelman osaan. Tämä liittyy käsitykseen yhden sivun ohjelmista (katso luku \ref{sect:spa}), joissa selain suorittaa yhden ohjelman, jonka jälkeen perinteisiä sivujen latauksia ei enää tapahdu. Ohjelma muokkaa osoitepalkissa näkyvää osoitetta, jonka pohjalta sitten reititys oikeaan komponenttiin tapahtuu. Käyttöliittymää muokataan käyttämällä DOM:ia, joka mahdollistaa sivun sisällön muokkaamisen ilman sivun uudelleen lataamista.
Asiakaspuolen reititys vaatii palvelinpuolelta sen, että kaikki mahdolliset asiakaspuolen reitit palauttavat ohjelman. Toinen edellytys on se, että palvelin ei palauta uudelleenohjausta (HTTP 302), koska silloin myös osoitepalkissa oleva osoite muuttuu, eikä sitä silloin voida välittää asiakaspuolen ohjelmalle.
Yritin pitkään toteuttaa tällaista logiikkaa palvelinpuolelle siinä onnistumatta, mutta onnekseni yew\char`_routerin esimerkeistä löytyi esimerkkikoodia tämän saavuttamiseksi käyttämäni palvelinkehyksen kanssa. Päädyin laittamaan kaikki palvelimen omat reitit polun \mintinline{shell}{/api/} alle, ja asiakaspuolen ohjelman juureen (\mintinline{shell}{/}). Sitten määritin ns. ''catch-all'' reitin, joka palauttaa asiakaspuolen ohjelman ilman uudelleenohjausta. Tämä on Actixissa nimeltään \mintinline{shell}{default_service}.
Myös yew\char`_routerin kanssa oli omat haasteensa. Alkuun en saanut sitä toimimaan ollenkaan, reititin ohjasi kaikki osoitteet ensimmäisenä määritettyyn reittiin, mikä oli tässä tapauksessa ''/''. Ongelman syyksi paljastui yew\char`_routerin Switch-komponentin kokoava ''to'-makro (katso luku \ref{chap:macros}). Ratkaisuna oli reitin ''/'' makron siirtäminen viimeiseksi listassa. Tarkempaa tietoa tästä ongelmasta voi lukea Yew:n dokumentaatiosta \cite{yew:router-problem}.
Asiakaspuolen reitityksen toteutuksen voi nähdä liitteessä \ref{appx:frontend-main}.
\section{Ongelmat}
Projektin loppuvaiheilla ilmeni muutamia ongelmia, jotka vaativat vielä jatkokehitystä.
\subsection{Evästeet}
\label{sect:problems:cookies}
Projektin loppuvaiheilla ilmeni muutamia ongelmia, joita en ehtinyt enää ajan puutteen takia korjaamaan.
Palvelinpuolella evästeen kokoava kirjasto, Actixin liitännäinen actix\char`_identity, kirjoittaa oletuksena evästeeseen parametrin \mintinline{JavaScript}{"HttpOnly": true}, mikä tarkoittaa sitä, että selain ei anna sen sisällä suoritettaville ohjelmille pääsyä tähän evästeeseen. Tämä on todella hyvä turvallisuusominaisuus, joka estää haitallisia ohjelmia varastamasta käyttäjän kirjautumistietoja.
Suunnittelmana oli käyttää evästeen tietoja asiakaspuolella reititykseen. Esimerkiksi jos evästettä ei ole olemassa, käyttäjä tulee reitittää sisäänkirjautumiskomponenttiin, jossa eväste voidaan noutaa palvelimelta. Koska evästeessä on tämä HttpOnly-parametri, tähän ei ole mahdollisuutta. Tämän parametrin voisi tietysti laittaa pois päältä, mutta actix\char`_identity ei tarjoa tähän mitään mahdollisuutta.
Yhtenä ratkaisuna tähän ongelmaan olisi käyttää JSON Web Tokenin tallettamiseen evästeen sijasta selaimen LocalStoragea. Tämä vaatisi lisää työtä sekä asiakaspuolella että palvelinpuolella ja valitettavasti tähän ei jäänyt enää aikaa.
Yhtenä ratkaisuna tähän ongelmaan olisi käyttää JSON Web Tokenin tallettamiseen evästeen sijasta selaimen LocalStoragea. Tämä vaatisi lisää työtä sekä asiakaspuolella että palvelinpuolella.
\subsection{Tietokantayhteys}
Palvelimen ja tietokannan välisessä yhteydessä oli koko projektin ajan ongelma, että jokainen kysely tietokantaan kesti noin viisi sekuntia. Luulen että tämä ongelma liittyy jotenkin käyttämääni yhteyksien yhdistämiseen (luku \ref{project:database}), koska palvelimen loki näyttää satunnaisesti virheviestin \mintinline{shell}{Failed to create DB pool: Error(Some("Too many connections")}. Ajan loppumisen takia tämä ongelma jäi korjaamatta.
\clearpage
Palvelimen ja tietokannan välisessä yhteydessä oli koko projektin ajan ongelma, että jokainen kysely tietokantaan kesti noin viisi sekuntia. Luulen että tämä ongelma liittyy jotenkin käyttämääni yhteyksien yhdistämiseen (luku \ref{project:database}), koska palvelimen loki näyttää satunnaisesti virheviestin \mintinline{shell}{Failed to create DB pool: Error(Some("Too many connections")}. Asian selvittäminen jää jatkokehitykseen.
\section{Tulokset}
Tuloksena syntyi yksinkertainen web-ohjelma, jossa on toimiva sisäänkirjautumistoiminallisuus, jonka avulla palvelimelta asiakas voi noutaa suojattua dataa. Projektissa sekä palvelin- että asiakaspuoli on toteutettu Rustilla. Kuvassa \ref{fig:login-process} on määritelty kaikki kirjautumisprosessin vaiheet.
\begin{figure}[h]
\centering
\includegraphics[width=\linewidth]{illustration/login-process.pdf}
@ -134,7 +155,8 @@ Palvelimen ja tietokannan välisessä yhteydessä oli koko projektin ajan ongelm
\label{fig:login-process}
\end{figure}
Tuloksena syntyi yksinkertainen web-ohjelma, jossa sekä palvelin- että asiakaspuoli on toteutettu Rustilla. Kuvassa \ref{fig:login-process} on määritelty kaikki kirjautumisprosessin vaiheet.
\clearpage
Ohjelman juuressa renderöidään komponentti ''ProtectedComponent'', missä on yksi nappi, jolla voidaan noutaa dataa palvelimelta. Tämä data on suojattu niin, että pyynnön mukana pitää olla eväste, jonka sisällä on JWT, joka sitten validoidaan palvelinpuolella. Jos validointi onnistuu, suojattu data lähetetään asiakkaalle. Asiakaspäässä HTTP-statuskoodin perusteella näytetään joko noudettu data, tai "403 Unauthorized".

View file

@ -3,7 +3,7 @@
\label{sect:soveltuvuus:palvelinpuoli}
Palvelinpuolella en näe Rustin käyttämisessä mitään varjopuolia. Jos palvelin tulee osaksi isoja nettisivuja, missä on päivittäin tuhansia kävijöitä, Rustin suorituskyvystä ja turvallisesta rinnakkaisajosta voi olla todella paljon hyötyä. Lisäksi jos palvelin tekee joitakin laskennallisesti raskaita tehtäviä, kuten vaikkapa 3D-mallinnusta tai koneoppimista, Rustin suorituskykyetu tulee entistäkin selvemmäksi.
Näen myös, että Rustin vahvasta tyypityksestä voi olla paljon hyötyä web-sovelluksen palvelinpuksen palvelinpuoltaa rakennettaessa. Kuten koodiesimerkissä \ref{code:rust:registeruser} nähtiin, vahvan tyypityksen tuomia etuja voi käyttää hyödykseen rajapintaa rakennettaessa. Lisäksi vahva tyypitys lisää ohjelman luotettavuutta vähentämällä ajonaikaisten virheiden riskiä. Vahvan tyypityksen voi kylläkin lisätä myös JavaScriptiin käyttämällä esimerkiksi TypeScriptiä \cite{typescript}.
Näen myös, että Rustin vahvasta tyypityksestä voi olla paljon hyötyä web-sovelluksen palvelinpuolta rakennettaessa. Kuten koodiesimerkissä \ref{code:rust:registeruser} nähtiin, vahvan tyypityksen tuomia etuja voi käyttää hyödykseen rajapintaa rakennettaessa. Lisäksi vahva tyypitys lisää ohjelman luotettavuutta vähentämällä ajonaikaisten virheiden riskiä. Vahvan tyypityksen voi kylläkin lisätä myös JavaScriptiin käyttämällä esimerkiksi TypeScriptiä \cite{typescript}.
Lisäksi Rust on suosittu kieli muuallakin kuin web-maailmassa. Tämän ansiosta kirjastojen saatavuus erityisesti palvelinpuolella ei ole ongelma. Rust-koodiin voi myös sisällyttää C- ja C++ -kirjastoja, jos jotakin projektissa tarvittavaa kirjastoa ei löydy Rust-pakettina.

View file

@ -1,11 +1,9 @@
\chapter{Yhteenveto}
Rust soveltuu todella hyvin palvelinpuolen web-ohjelmointikieleksi. Sen luotettavuus, suorituskyky ja rinnakkaisajo-ominaisuudet tuovat todellisia hyötyjä sekä kehittäjille että palvelimen asiakkaille.
Rust on asiakaspuolen web-ohjelmointikielenä riippuvainen WebAssembly-ekosysteemistä, minkä takia kirjastojen saatavuus on vielä melko huono. Lisäksi asiakaspuolen alhaisten suorituskykyvaatimuksien takia Rustin paremmasta suorituskyvystä verrattuna JavaScriptiin on vaikea oikeuttaa useimmissa tapauksissa.
Rust on asiakaspuolen web-ohjelmointikielenä riippuvainen WebAssembly-ekosysteemistä, minkä takia kirjastojen saatavuus on vielä melko huono. Lisäksi asiakaspuolen alhaisten suorituskykyvaatimuksien takia Rustin paremmasta suorituskyvystä verrattuna JavaScriptiin on vähän hyötyä useimmissa tapauksissa.
Koko projektia ei ole pakko kirjoittaa alusta loppuun Rustilla, niinkuin tässä insinöörityössä tehtiin. Jos asiakaspuolen ohjelmassa on korkeampaa suorituskykyä vaativia osia, ne voi kirjoittaa Rustilla ja sisälllyttää WebAssembly-binääreinä osaksi muuten JavaScriptillä toteutettua ohjelmaa.
Myös palvelinpuolella on mahdollista toteuttaa suurin osa projektista JavaScriptilla, ja sisällyttää sitten parempaa suorituskykyä vaativat, Rustilla kirjoitetut ohjelman osat osaksi JavaScript-koodia.
Koko projektia ei ole pakko kirjoittaa alusta loppuun Rustilla, niinkuin tässä insinöörityössä tehtiin. Jos ohjelmassa on korkeampaa suorituskykyä vaativia osia, ne voi kirjoittaa Rustilla ja sisälllyttää WebAssembly-binääreinä osaksi muuten JavaScriptillä toteutettua ohjelmaa.
Rustia voi sisällyttää edellä mainituilla tekniikoilla myös olemassa oleviin projekteihin. Jos kehittäjä haluaa siirtää olemassa olevan JavaScript-projektin kokonaan Rust-pohjaiseksi, sen voi tehdä asteittain.

View file

@ -1 +0,0 @@
../../project

1
tex/code/project/backend Symbolic link
View file

@ -0,0 +1 @@
../../../project/backend/src

1
tex/code/project/frontend Symbolic link
View file

@ -0,0 +1 @@
../../../project/frontend/src

View file

@ -1,2 +1,2 @@
let name = "Marko"; // string slice
let age = 26; // integer
let name = "Marko"; // Merkkijono
let age = 26; // Kokonaisluku

View file

@ -8,6 +8,8 @@ xelatex -shell-escape -8bit main
makeglossaries main
find chapters | entr -cp xelatex -shell-escape -8bit main &
find appendix | entr -cp xelatex -shell-escape -8bit main &
find code | entr -cp xelatex -shell-escape -8bit main &
ls biblio.bib | entr -cp bibtex8 main &
zathura >/dev/null 2>&1 main.pdf