2023-03-28 23:55:57 +03:00
|
|
|
// Copyright (c) 2019-2023 Alexander Medvednikov. All rights reserved.
|
2021-06-06 00:43:14 +03:00
|
|
|
// Use of this source code is governed by an MIT license
|
|
|
|
// that can be found in the LICENSE file.
|
|
|
|
module http
|
|
|
|
|
|
|
|
import io
|
|
|
|
import net
|
|
|
|
import time
|
2023-05-27 01:57:32 +03:00
|
|
|
import runtime
|
2021-08-24 19:21:24 +03:00
|
|
|
// ServerStatus is the current status of the server.
|
|
|
|
// .running means that the server is active and serving.
|
|
|
|
// .stopped means that the server is not active but still listening.
|
|
|
|
// .closed means that the server is completely inactive.
|
2023-05-27 01:57:32 +03:00
|
|
|
|
2021-08-24 19:21:24 +03:00
|
|
|
pub enum ServerStatus {
|
|
|
|
running
|
|
|
|
stopped
|
|
|
|
closed
|
|
|
|
}
|
|
|
|
|
2021-08-01 15:50:11 +03:00
|
|
|
interface Handler {
|
2021-10-11 15:41:31 +03:00
|
|
|
mut:
|
2021-08-01 15:50:11 +03:00
|
|
|
handle(Request) Response
|
|
|
|
}
|
|
|
|
|
2021-06-06 00:43:14 +03:00
|
|
|
pub struct Server {
|
2021-08-24 19:21:24 +03:00
|
|
|
mut:
|
2023-07-16 15:42:30 +03:00
|
|
|
state ServerStatus = .closed
|
2021-06-06 00:43:14 +03:00
|
|
|
pub mut:
|
2023-07-16 15:42:30 +03:00
|
|
|
addr string = ':8080' // change to ':8080' when port is removed
|
|
|
|
port int [deprecated: 'use addr'] = 8080
|
2023-05-27 01:57:32 +03:00
|
|
|
handler Handler = DebugHandler{}
|
|
|
|
read_timeout time.Duration = 30 * time.second
|
|
|
|
write_timeout time.Duration = 30 * time.second
|
|
|
|
accept_timeout time.Duration = 30 * time.second
|
|
|
|
pool_channel_slots int = 1024
|
|
|
|
worker_num int = runtime.nr_jobs()
|
2023-07-16 15:42:30 +03:00
|
|
|
listener net.TcpListener
|
2021-06-06 00:43:14 +03:00
|
|
|
}
|
|
|
|
|
2023-01-27 14:58:55 +03:00
|
|
|
// listen_and_serve listens on the server port `s.port` over TCP network and
|
|
|
|
// uses `s.parse_and_respond` to handle requests on incoming connections with `s.handler`.
|
2022-10-16 09:28:57 +03:00
|
|
|
pub fn (mut s Server) listen_and_serve() {
|
2021-08-01 15:50:11 +03:00
|
|
|
if s.handler is DebugHandler {
|
2021-06-06 00:43:14 +03:00
|
|
|
eprintln('Server handler not set, using debug handler')
|
|
|
|
}
|
2023-07-16 15:42:30 +03:00
|
|
|
|
|
|
|
// remove when s.port is removed
|
|
|
|
addr := s.addr.split(':')
|
|
|
|
if addr.len > 1 && s.port != 8080 {
|
|
|
|
s.addr = '${addr[0]}:${s.port}'
|
|
|
|
}
|
|
|
|
|
|
|
|
mut l := s.listener.addr() or {
|
|
|
|
eprintln('Failed getting listener address')
|
2022-10-16 09:28:57 +03:00
|
|
|
return
|
|
|
|
}
|
2023-07-16 15:42:30 +03:00
|
|
|
if l.family() == net.AddrFamily.unspec {
|
|
|
|
s.listener = net.listen_tcp(.ip6, '${s.addr}') or {
|
|
|
|
eprintln('Listening on ${s.addr} failed')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
l = s.listener.addr() or {
|
|
|
|
eprintln('Failed getting listener address')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.addr = l.str()
|
2021-08-24 19:21:24 +03:00
|
|
|
s.listener.set_accept_timeout(s.accept_timeout)
|
2023-05-27 01:57:32 +03:00
|
|
|
|
|
|
|
// Create tcp connection channel
|
|
|
|
ch := chan &net.TcpConn{cap: s.pool_channel_slots}
|
|
|
|
|
|
|
|
// Create workers
|
|
|
|
mut ws := []thread{cap: s.worker_num}
|
|
|
|
for wid in 0 .. s.worker_num {
|
|
|
|
ws << new_handler_worker(wid, ch, s.handler)
|
|
|
|
}
|
|
|
|
|
2023-07-16 15:42:30 +03:00
|
|
|
eprintln('Listening on ${s.addr}')
|
2021-08-24 19:21:24 +03:00
|
|
|
s.state = .running
|
2021-06-06 00:43:14 +03:00
|
|
|
for {
|
2021-08-24 19:21:24 +03:00
|
|
|
// break if we have a stop signal
|
|
|
|
if s.state != .running {
|
|
|
|
break
|
2021-08-19 11:19:49 +03:00
|
|
|
}
|
2021-08-24 19:21:24 +03:00
|
|
|
mut conn := s.listener.accept() or {
|
2022-10-23 17:08:16 +03:00
|
|
|
if err.code() == net.err_timed_out_code {
|
|
|
|
// just skip network timeouts, they are normal
|
|
|
|
continue
|
2021-08-19 11:19:49 +03:00
|
|
|
}
|
2022-11-15 16:53:13 +03:00
|
|
|
eprintln('accept() failed, reason: ${err}; skipping')
|
2021-06-06 00:43:14 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
conn.set_read_timeout(s.read_timeout)
|
|
|
|
conn.set_write_timeout(s.write_timeout)
|
2023-05-27 01:57:32 +03:00
|
|
|
ch <- conn
|
2021-06-06 00:43:14 +03:00
|
|
|
}
|
2021-08-24 19:21:24 +03:00
|
|
|
if s.state == .stopped {
|
|
|
|
s.close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-27 14:58:55 +03:00
|
|
|
// stop signals the server that it should not respond anymore.
|
2021-08-24 19:21:24 +03:00
|
|
|
[inline]
|
|
|
|
pub fn (mut s Server) stop() {
|
|
|
|
s.state = .stopped
|
|
|
|
}
|
|
|
|
|
2023-05-12 09:31:27 +03:00
|
|
|
// close immediately closes the port and signals the server that it has been closed.
|
2021-08-24 19:21:24 +03:00
|
|
|
[inline]
|
|
|
|
pub fn (mut s Server) close() {
|
|
|
|
s.state = .closed
|
|
|
|
s.listener.close() or { return }
|
2021-06-06 00:43:14 +03:00
|
|
|
}
|
|
|
|
|
2023-01-27 14:58:55 +03:00
|
|
|
// status indicates whether the server is running, stopped, or closed.
|
2021-08-24 19:21:24 +03:00
|
|
|
[inline]
|
|
|
|
pub fn (s &Server) status() ServerStatus {
|
|
|
|
return s.state
|
2021-08-19 11:19:49 +03:00
|
|
|
}
|
|
|
|
|
2023-05-27 01:57:32 +03:00
|
|
|
struct HandlerWorker {
|
|
|
|
id int
|
|
|
|
ch chan &net.TcpConn
|
|
|
|
pub mut:
|
|
|
|
handler Handler
|
|
|
|
}
|
|
|
|
|
|
|
|
fn new_handler_worker(wid int, ch chan &net.TcpConn, handler Handler) thread {
|
|
|
|
mut w := &HandlerWorker{
|
|
|
|
id: wid
|
|
|
|
ch: ch
|
|
|
|
handler: handler
|
|
|
|
}
|
|
|
|
return spawn w.process_requests()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut w HandlerWorker) process_requests() {
|
|
|
|
for {
|
|
|
|
mut conn := <-w.ch or { break }
|
|
|
|
w.handle_conn(mut conn)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut w HandlerWorker) handle_conn(mut conn net.TcpConn) {
|
2021-06-06 00:43:14 +03:00
|
|
|
defer {
|
2022-11-15 16:53:13 +03:00
|
|
|
conn.close() or { eprintln('close() failed: ${err}') }
|
2021-06-06 00:43:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
mut reader := io.new_buffered_reader(reader: conn)
|
|
|
|
defer {
|
2022-08-20 08:03:07 +03:00
|
|
|
unsafe {
|
|
|
|
reader.free()
|
|
|
|
}
|
2021-06-06 00:43:14 +03:00
|
|
|
}
|
2023-05-21 16:23:24 +03:00
|
|
|
mut req := parse_request(mut reader) or {
|
2021-06-06 00:43:14 +03:00
|
|
|
$if debug {
|
|
|
|
// only show in debug mode to prevent abuse
|
2022-11-15 16:53:13 +03:00
|
|
|
eprintln('error parsing request: ${err}')
|
2021-06-06 00:43:14 +03:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2023-05-21 16:23:24 +03:00
|
|
|
|
|
|
|
remote_ip := conn.peer_ip() or { '' }
|
|
|
|
req.header.add_custom('Remote-Addr', remote_ip) or {}
|
|
|
|
|
2023-05-27 01:57:32 +03:00
|
|
|
mut resp := w.handler.handle(req)
|
2021-07-24 20:47:45 +03:00
|
|
|
if resp.version() == .unknown {
|
|
|
|
resp.set_version(req.version)
|
2021-06-06 00:43:14 +03:00
|
|
|
}
|
2023-05-27 01:57:32 +03:00
|
|
|
|
|
|
|
// Implemented by developers?
|
|
|
|
if !resp.header.contains(.content_length) {
|
|
|
|
resp.header.set(.content_length, '${resp.body.len}')
|
|
|
|
}
|
|
|
|
|
2022-11-15 16:53:13 +03:00
|
|
|
conn.write(resp.bytes()) or { eprintln('error sending response: ${err}') }
|
2021-06-06 00:43:14 +03:00
|
|
|
}
|
2021-08-01 15:50:11 +03:00
|
|
|
|
|
|
|
// DebugHandler implements the Handler interface by echoing the request
|
2023-01-27 14:58:55 +03:00
|
|
|
// in the response.
|
2021-08-01 15:50:11 +03:00
|
|
|
struct DebugHandler {}
|
|
|
|
|
|
|
|
fn (d DebugHandler) handle(req Request) Response {
|
|
|
|
$if debug {
|
2022-11-15 16:53:13 +03:00
|
|
|
eprintln('[${time.now()}] ${req.method} ${req.url}\n\r${req.header}\n\r${req.data} - 200 OK')
|
2021-08-01 15:50:11 +03:00
|
|
|
} $else {
|
2022-11-15 16:53:13 +03:00
|
|
|
eprintln('[${time.now()}] ${req.method} ${req.url} - 200')
|
2021-08-01 15:50:11 +03:00
|
|
|
}
|
|
|
|
mut r := Response{
|
2022-05-29 20:27:18 +03:00
|
|
|
body: req.data
|
2021-08-01 15:50:11 +03:00
|
|
|
header: req.header
|
|
|
|
}
|
|
|
|
r.set_status(.ok)
|
|
|
|
r.set_version(req.version)
|
|
|
|
return r
|
|
|
|
}
|