vweb: first step to revive it
parent
2032da7fe2
commit
892d1c6aab
|
@ -3,7 +3,7 @@ module main
|
||||||
import vweb
|
import vweb
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Port = 8082
|
port = 8082
|
||||||
)
|
)
|
||||||
|
|
||||||
struct App {
|
struct App {
|
||||||
|
@ -13,7 +13,9 @@ pub mut:
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
vweb.run<App>(Port)
|
mut app := App{}
|
||||||
|
vweb.run(app, port)
|
||||||
|
//vweb.run<App>(Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (app mut App) init() {
|
pub fn (app mut App) init() {
|
|
@ -275,8 +275,8 @@ fn (p mut Parser) fn_decl() {
|
||||||
// C function header def? (fn C.NSMakeRect(int,int,int,int))
|
// C function header def? (fn C.NSMakeRect(int,int,int,int))
|
||||||
is_c := f.name == 'C' && p.tok == .dot
|
is_c := f.name == 'C' && p.tok == .dot
|
||||||
// Just fn signature? only builtin.v + default build mode
|
// Just fn signature? only builtin.v + default build mode
|
||||||
if p.pref.build_mode == .build_module {
|
if p.pref.is_verbose { // p.pref.build_mode == .build_module {
|
||||||
//println('\n\nfn_decl() name=$f.name receiver_typ=$receiver_typ nogen=$p.cgen.nogen')
|
println('\n\nfn_decl() name=$f.name receiver_typ=$receiver_typ nogen=$p.cgen.nogen')
|
||||||
}
|
}
|
||||||
if is_c {
|
if is_c {
|
||||||
p.check(.dot)
|
p.check(.dot)
|
||||||
|
@ -561,8 +561,10 @@ fn (p mut Parser) check_unused_variables() {
|
||||||
if !var.is_used && !p.pref.is_repl && !var.is_arg && !p.pref.translated {
|
if !var.is_used && !p.pref.is_repl && !var.is_arg && !p.pref.translated {
|
||||||
p.production_error_with_token_index('`$var.name` declared and not used', var.token_idx )
|
p.production_error_with_token_index('`$var.name` declared and not used', var.token_idx )
|
||||||
}
|
}
|
||||||
if !var.is_changed && var.is_mut && !p.pref.is_repl && !p.pref.translated {
|
if !var.is_changed && var.is_mut && !p.pref.is_repl &&
|
||||||
p.error_with_token_index( '`$var.name` is declared as mutable, but it was never changed', var.token_idx )
|
!p.pref.translated && var.typ != 'T*'
|
||||||
|
{
|
||||||
|
p.error_with_token_index('`$var.name` is declared as mutable, but it was never changed', var.token_idx )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -472,7 +472,7 @@ fn (p mut Parser) import_statement() {
|
||||||
if p.tok != .name {
|
if p.tok != .name {
|
||||||
p.error('bad import format')
|
p.error('bad import format')
|
||||||
}
|
}
|
||||||
if p.peek() == .number { // && p.scanner.text[p.scanner.pos + 1] == `.` {
|
if p.peek() == .number {
|
||||||
p.error('bad import format. module/submodule names cannot begin with a number')
|
p.error('bad import format. module/submodule names cannot begin with a number')
|
||||||
}
|
}
|
||||||
import_tok_idx := p.token_idx-1
|
import_tok_idx := p.token_idx-1
|
||||||
|
@ -513,14 +513,6 @@ fn (p mut Parser) const_decl() {
|
||||||
if is_pub {
|
if is_pub {
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
if p.tok == .key_import {
|
|
||||||
p.error_with_token_index(
|
|
||||||
'`import const` was removed from the language, ' +
|
|
||||||
'because predeclaring C constants is not needed anymore. ' +
|
|
||||||
'You can use them directly with C.CONST_NAME',
|
|
||||||
p.cur_tok_index()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
p.inside_const = true
|
p.inside_const = true
|
||||||
p.check(.key_const)
|
p.check(.key_const)
|
||||||
p.fspace()
|
p.fspace()
|
||||||
|
@ -659,13 +651,13 @@ fn (p mut Parser) interface_method(field_name, receiver string) &Fn {
|
||||||
|
|
||||||
fn key_to_type_cat(tok TokenKind) TypeCategory {
|
fn key_to_type_cat(tok TokenKind) TypeCategory {
|
||||||
match tok {
|
match tok {
|
||||||
.key_interface { return TypeCategory.interface_ }
|
.key_interface { return .interface_ }
|
||||||
.key_struct { return TypeCategory.struct_ }
|
.key_struct { return .struct_ }
|
||||||
.key_union { return TypeCategory.union_ }
|
.key_union { return .union_ }
|
||||||
//TokenKind.key_ => return .interface_
|
//TokenKind.key_ => return .interface_
|
||||||
}
|
}
|
||||||
verror('Unknown token: $tok')
|
verror('Unknown token: $tok')
|
||||||
return TypeCategory.builtin
|
return .builtin
|
||||||
}
|
}
|
||||||
|
|
||||||
// check_name checks for a name token and returns its literal
|
// check_name checks for a name token and returns its literal
|
||||||
|
@ -727,10 +719,10 @@ fn (p mut Parser) check(expected TokenKind) {
|
||||||
*/
|
*/
|
||||||
p.next()
|
p.next()
|
||||||
|
|
||||||
if p.scanner.line_comment != '' {
|
//if p.scanner.line_comment != '' {
|
||||||
//p.fgenln('// ! "$p.scanner.line_comment"')
|
//p.fgenln('// ! "$p.scanner.line_comment"')
|
||||||
//p.scanner.line_comment = ''
|
//p.scanner.line_comment = ''
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -894,7 +886,7 @@ fn (p mut Parser) get_type() string {
|
||||||
p.error('unknown type `$typ`')
|
p.error('unknown type `$typ`')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if !t.is_public && t.mod != p.mod && t.name != '' {
|
else if !t.is_public && t.mod != p.mod && t.name != '' && !p.first_pass() {
|
||||||
p.error('type `$t.name` is private')
|
p.error('type `$t.name` is private')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,7 @@ fn (p mut Parser) struct_decl() {
|
||||||
typ.is_placeholder = false
|
typ.is_placeholder = false
|
||||||
typ.cat = cat
|
typ.cat = cat
|
||||||
typ.parent = objc_parent
|
typ.parent = objc_parent
|
||||||
|
typ.is_public = is_pub
|
||||||
p.table.rewrite_type(typ)
|
p.table.rewrite_type(typ)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -11,9 +11,9 @@ const (
|
||||||
max_redirects = 4
|
max_redirects = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
struct Request {
|
pub struct Request {
|
||||||
pub:
|
pub:
|
||||||
headers map[string]string
|
headers map[string]string
|
||||||
method string
|
method string
|
||||||
// cookies map[string]string
|
// cookies map[string]string
|
||||||
h string
|
h string
|
||||||
|
@ -27,10 +27,10 @@ pub:
|
||||||
user_agent string
|
user_agent string
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Response {
|
pub struct Response {
|
||||||
pub:
|
pub:
|
||||||
text string
|
text string
|
||||||
headers map[string]string
|
headers map[string]string
|
||||||
status_code int
|
status_code int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ pub fn post(url, data string) ?Response {
|
||||||
|
|
||||||
pub fn new_request(typ, _url, _data string) ?Request {
|
pub fn new_request(typ, _url, _data string) ?Request {
|
||||||
if _url == '' {
|
if _url == '' {
|
||||||
return error('bad url')
|
return error('bad url')
|
||||||
}
|
}
|
||||||
mut url := _url
|
mut url := _url
|
||||||
mut data := _data
|
mut data := _data
|
||||||
|
@ -77,9 +77,9 @@ pub fn new_request(typ, _url, _data string) ?Request {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_text(url string) string {
|
pub fn get_text(url string) string {
|
||||||
resp := get(url) or { return '' }
|
resp := get(url) or { return '' }
|
||||||
return resp.text
|
return resp.text
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (req mut Request) free() {
|
fn (req mut Request) free() {
|
||||||
req.headers.free()
|
req.headers.free()
|
||||||
|
@ -172,29 +172,29 @@ fn (req &Request) method_and_url_to_response(method string, url net_dot_urllib.U
|
||||||
|
|
||||||
fn parse_response(resp string) Response {
|
fn parse_response(resp string) Response {
|
||||||
mut headers := map[string]string
|
mut headers := map[string]string
|
||||||
first_header := resp.all_before('\n')
|
first_header := resp.all_before('\n')
|
||||||
mut status_code := 0
|
mut status_code := 0
|
||||||
if first_header.contains('HTTP/') {
|
if first_header.contains('HTTP/') {
|
||||||
val := first_header.find_between(' ', ' ')
|
val := first_header.find_between(' ', ' ')
|
||||||
status_code = val.int()
|
status_code = val.int()
|
||||||
}
|
}
|
||||||
mut text := ''
|
mut text := ''
|
||||||
// Build resp headers map and separate the body
|
// Build resp headers map and separate the body
|
||||||
mut nl_pos := 3
|
mut nl_pos := 3
|
||||||
mut i := 1
|
mut i := 1
|
||||||
for {
|
for {
|
||||||
old_pos := nl_pos
|
old_pos := nl_pos
|
||||||
nl_pos = resp.index_after('\n', nl_pos+1)
|
nl_pos = resp.index_after('\n', nl_pos+1)
|
||||||
if nl_pos == -1 {
|
if nl_pos == -1 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
h := resp.substr(old_pos + 1, nl_pos)
|
h := resp.substr(old_pos + 1, nl_pos)
|
||||||
// End of headers
|
// End of headers
|
||||||
if h.len <= 1 {
|
if h.len <= 1 {
|
||||||
text = resp.right(nl_pos + 1)
|
text = resp.right(nl_pos + 1)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
pos := h.index(':')
|
pos := h.index(':')
|
||||||
if pos == -1 {
|
if pos == -1 {
|
||||||
continue
|
continue
|
||||||
|
@ -212,9 +212,9 @@ fn parse_response(resp string) Response {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response {
|
return Response {
|
||||||
status_code: status_code
|
status_code: status_code
|
||||||
headers: headers
|
headers: headers
|
||||||
text: text
|
text: text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,33 +222,33 @@ fn (req &Request) build_request_headers(method, host_name, path string) string {
|
||||||
ua := req.user_agent
|
ua := req.user_agent
|
||||||
mut uheaders := []string
|
mut uheaders := []string
|
||||||
for key, val in req.headers {
|
for key, val in req.headers {
|
||||||
uheaders << '${key}: ${val}\r\n'
|
uheaders << '${key}: ${val}\r\n'
|
||||||
}
|
}
|
||||||
if req.data.len > 0 {
|
if req.data.len > 0 {
|
||||||
uheaders << 'Content-Length: ${req.data.len}\r\n'
|
uheaders << 'Content-Length: ${req.data.len}\r\n'
|
||||||
}
|
}
|
||||||
return '$method $path HTTP/1.1\r\n' +
|
return '$method $path HTTP/1.1\r\n' +
|
||||||
'Host: $host_name\r\n' +
|
'Host: $host_name\r\n' +
|
||||||
'User-Agent: $ua\r\n' +
|
'User-Agent: $ua\r\n' +
|
||||||
uheaders.join('') +
|
uheaders.join('') +
|
||||||
'Connection: close\r\n\r\n' +
|
'Connection: close\r\n\r\n' +
|
||||||
req.data
|
req.data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unescape_url(s string) string {
|
pub fn unescape_url(s string) string {
|
||||||
panic('http.unescape_url() was replaced with urllib.query_unescape()')
|
panic('http.unescape_url() was replaced with urllib.query_unescape()')
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn escape_url(s string) string {
|
pub fn escape_url(s string) string {
|
||||||
panic('http.escape_url() was replaced with urllib.query_escape()')
|
panic('http.escape_url() was replaced with urllib.query_escape()')
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unescape(s string) string {
|
pub fn unescape(s string) string {
|
||||||
panic('http.unescape() was replaced with http.unescape_url()')
|
panic('http.unescape() was replaced with http.unescape_url()')
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn escape(s string) string {
|
pub fn escape(s string) string {
|
||||||
panic('http.escape() was replaced with http.escape_url()')
|
panic('http.escape() was replaced with http.escape_url()')
|
||||||
}
|
}
|
||||||
|
|
||||||
type wsfn fn (s string, ptr voidptr)
|
type wsfn fn (s string, ptr voidptr)
|
||||||
|
|
|
@ -2,7 +2,7 @@ module net
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
struct Socket {
|
pub struct Socket {
|
||||||
pub:
|
pub:
|
||||||
sockfd int
|
sockfd int
|
||||||
family int
|
family int
|
||||||
|
|
111
vlib/vweb/vweb.v
111
vlib/vweb/vweb.v
|
@ -1,11 +1,15 @@
|
||||||
|
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT license
|
||||||
|
// that can be found in the LICENSE file.
|
||||||
|
|
||||||
module vweb
|
module vweb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
os
|
os
|
||||||
net
|
net
|
||||||
http
|
http
|
||||||
net.urllib
|
net.urllib
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
methods_with_form = ['POST', 'PUT', 'PATCH']
|
methods_with_form = ['POST', 'PUT', 'PATCH']
|
||||||
|
@ -27,16 +31,16 @@ const (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
struct Context {
|
pub struct Context {
|
||||||
static_files map[string]string
|
static_files map[string]string
|
||||||
static_mime_types map[string]string
|
static_mime_types map[string]string
|
||||||
pub:
|
pub:
|
||||||
req http.Request
|
req http.Request
|
||||||
conn net.Socket
|
conn net.Socket
|
||||||
form map[string]string
|
form map[string]string
|
||||||
// TODO Response
|
// TODO Response
|
||||||
mut:
|
mut:
|
||||||
headers string // response headers
|
headers string // response headers
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (ctx Context) html(html string) {
|
pub fn (ctx Context) html(html string) {
|
||||||
|
@ -48,11 +52,11 @@ pub fn (ctx Context) text(s string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (ctx Context) json(s string) {
|
pub fn (ctx Context) json(s string) {
|
||||||
ctx.conn.write('HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n$ctx.headers\r\n\r\n$s')
|
ctx.conn.write('HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n$ctx.headers\r\n\r\n$s')
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (ctx Context) redirect(url string) {
|
pub fn (ctx Context) redirect(url string) {
|
||||||
ctx.conn.write('HTTP/1.1 302 Found\r\nLocation: $url\r\n\r\n$ctx.headers')
|
ctx.conn.write('HTTP/1.1 302 Found\r\nLocation: $url\r\n\r\n$ctx.headers')
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (ctx Context) not_found(s string) {
|
pub fn (ctx Context) not_found(s string) {
|
||||||
|
@ -84,15 +88,16 @@ fn (ctx mut Context) get_header(key string) string {
|
||||||
return ctx.headers.find_between('\r\n$key: ', '\r\n')
|
return ctx.headers.find_between('\r\n$key: ', '\r\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run<T>(port int) {
|
//pub fn run<T>(port int) {
|
||||||
println('Running vweb app on http://localhost:$port ...')
|
pub fn run<T>(app T, port int) {
|
||||||
l := net.listen(port) or { panic('failed to listen') }
|
println('Running vweb app on http://localhost:$port ...')
|
||||||
mut app := T{}
|
l := net.listen(port) or { panic('failed to listen') }
|
||||||
app.init()
|
//mut app := T{}
|
||||||
|
app.init()
|
||||||
for {
|
for {
|
||||||
conn := l.accept() or {
|
conn := l.accept() or {
|
||||||
panic('accept() failed')
|
panic('accept() failed')
|
||||||
}
|
}
|
||||||
//foobar<T>()
|
//foobar<T>()
|
||||||
// TODO move this to handle_conn<T>(conn, app)
|
// TODO move this to handle_conn<T>(conn, app)
|
||||||
s := conn.read_line()
|
s := conn.read_line()
|
||||||
|
@ -123,35 +128,35 @@ pub fn run<T>(port int) {
|
||||||
ws_func: 0
|
ws_func: 0
|
||||||
user_ptr: 0
|
user_ptr: 0
|
||||||
method: vals[0]
|
method: vals[0]
|
||||||
url: vals[1]
|
url: vals[1]
|
||||||
}
|
}
|
||||||
$if debug {
|
$if debug {
|
||||||
println('vweb action = "$action"')
|
println('vweb action = "$action"')
|
||||||
}
|
}
|
||||||
//mut app := T{
|
//mut app := T{
|
||||||
app.vweb = Context{
|
app.vweb = Context{
|
||||||
req: req
|
req: req
|
||||||
conn: conn
|
conn: conn
|
||||||
form: map[string]string
|
form: map[string]string
|
||||||
static_files: map[string]string
|
static_files: map[string]string
|
||||||
static_mime_types: map[string]string
|
static_mime_types: map[string]string
|
||||||
}
|
}
|
||||||
//}
|
//}
|
||||||
if req.method in methods_with_form {
|
if req.method in methods_with_form {
|
||||||
app.vweb.parse_form(s)
|
app.vweb.parse_form(s)
|
||||||
}
|
}
|
||||||
if vals.len < 2 {
|
if vals.len < 2 {
|
||||||
$if debug {
|
$if debug {
|
||||||
println('no vals for http')
|
println('no vals for http')
|
||||||
}
|
}
|
||||||
conn.close()
|
conn.close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve a static file if it's one
|
// Serve a static file if it's one
|
||||||
// if app.vweb.handle_static() {
|
// if app.vweb.handle_static() {
|
||||||
// conn.close()
|
// conn.close()
|
||||||
// continue
|
// continue
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Call the right action
|
// Call the right action
|
||||||
|
@ -163,13 +168,13 @@ pub fn run<T>(port int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn foobar<T>() {
|
pub fn foobar<T>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (ctx mut Context) parse_form(s string) {
|
fn (ctx mut Context) parse_form(s string) {
|
||||||
if !(ctx.req.method in methods_with_form) {
|
if !(ctx.req.method in methods_with_form) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pos := s.index('\r\n\r\n')
|
pos := s.index('\r\n\r\n')
|
||||||
if pos > -1 {
|
if pos > -1 {
|
||||||
mut str_form := s.substr(pos, s.len)
|
mut str_form := s.substr(pos, s.len)
|
||||||
|
@ -177,18 +182,18 @@ fn (ctx mut Context) parse_form(s string) {
|
||||||
words := str_form.split('&')
|
words := str_form.split('&')
|
||||||
for word in words {
|
for word in words {
|
||||||
$if debug {
|
$if debug {
|
||||||
println('parse form keyval="$word"')
|
println('parse form keyval="$word"')
|
||||||
}
|
}
|
||||||
keyval := word.trim_space().split('=')
|
keyval := word.trim_space().split('=')
|
||||||
if keyval.len != 2 { continue }
|
if keyval.len != 2 { continue }
|
||||||
key := keyval[0]
|
key := keyval[0]
|
||||||
val := urllib.query_unescape(keyval[1]) or {
|
val := urllib.query_unescape(keyval[1]) or {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
$if debug {
|
$if debug {
|
||||||
println('http form "$key" => "$val"')
|
println('http form "$key" => "$val"')
|
||||||
}
|
}
|
||||||
ctx.form[key] = val
|
ctx.form[key] = val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,15 +232,15 @@ pub fn (ctx mut Context) handle_static(directory_path string) bool {
|
||||||
static_file := ctx.static_files[ctx.req.url]
|
static_file := ctx.static_files[ctx.req.url]
|
||||||
mime_type := ctx.static_mime_types[ctx.req.url]
|
mime_type := ctx.static_mime_types[ctx.req.url]
|
||||||
|
|
||||||
if static_file != '' {
|
if static_file != '' {
|
||||||
data := os.read_file(static_file) or { return false }
|
data := os.read_file(static_file) or { return false }
|
||||||
ctx.conn.write('HTTP/1.1 200 OK\r\nContent-Type: $mime_type\r\n\r\n$data')
|
ctx.conn.write('HTTP/1.1 200 OK\r\nContent-Type: $mime_type\r\n\r\n$data')
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (ctx mut Context) serve_static(url, file_path, mime_type string) {
|
pub fn (ctx mut Context) serve_static(url, file_path, mime_type string) {
|
||||||
ctx.static_files[url] = file_path
|
ctx.static_files[url] = file_path
|
||||||
ctx.static_mime_types[url] = mime_type
|
ctx.static_mime_types[url] = mime_type
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue