summary refs log tree commit diff stats
path: root/tests/async/tasynceverror.nim
blob: 22b4fe9a703e502d70fa2c0000944b60da3e5c34 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
discard """
  file: "tasynceverror.nim"
  exitcode: 1
  outputsub: "Error: unhandled exception: Connection reset by peer"
"""

import
    asyncdispatch,
    asyncnet,
    nativesockets,
    os


const
    testHost = "127.0.0.1"
    testPort = Port(17357)


when defined(windows) or defined(nimdoc):
    # TODO: just make it work on Windows for now.
    quit("Error: unhandled exception: Connection reset by peer")
else:
    proc createListenSocket(host: string, port: Port): TAsyncFD =
        result = newAsyncNativeSocket()

        SocketHandle(result).setSockOptInt(SOL_SOCKET, SO_REUSEADDR, 1)

        var aiList = getAddrInfo(host, port, AF_INET)
        if SocketHandle(result).bindAddr(aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
          dealloc(aiList)
          raiseOSError(osLastError())
        dealloc(aiList)

        if SocketHandle(result).listen(1) < 0'i32:
            raiseOSError(osLastError())


    proc testAsyncSend() {.async.} =
        var
            ls = createListenSocket(testHost, testPort)
            s = newAsyncSocket()

        await s.connect(testHost, testPort)

        var ps = await ls.accept()
        SocketHandle(ls).close()

        await ps.send("test 1", flags={})
        s.close()
        # This send should raise EPIPE
        await ps.send("test 2", flags={})
        SocketHandle(ps).close()


    # The bug was, when the poll function handled EvError for us,
    # our callbacks may never get executed, thus making the event
    # loop block indefinitely. This is a timer to keep everything
    # rolling. 400 ms is an arbitrary value, should be enough though.
    proc timer() {.async.} =
        await sleepAsync(400)
        echo("Timer expired.")
        quit(2)


    asyncCheck(testAsyncSend())
    waitFor(timer())
ef='#n100'>100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
188
189
190
191
192
193
194
195
196
                                                      

        
                      








                                             




                      







                      

                        

 
















                                                                



                                        








                                               
                

 
                     








                                                                   
                     
                      
                        

                    
                  

 
               
                      
                 
                    





































































































                                                                                           








                                   
package svc // import "github.com/getwtxt/getwtxt/svc"

import (
	"database/sql"
	"log"
	"net"
	"strings"
	"time"

	"github.com/getwtxt/registry"
	"github.com/syndtr/goleveldb/leveldb"
)

type dbase interface {
	push() error
	pull()
}

type dbLevel struct {
	db *leveldb.DB
}

type dbSqlite struct {
	db *sql.DB
}

type dbPostgres struct {
	db *sql.DB
}

// Pull DB data into cache, if available.
func initDatabase() {
	var db dbase
	var err error

	confObj.Mu.RLock()
	switch confObj.DBType {

	case "leveldb":
		var lvl *leveldb.DB
		lvl, err = leveldb.OpenFile(confObj.DBPath, nil)
		db = &dbLevel{db: lvl}

	case "sqlite":
		var lite *sql.DB
		db = &dbSqlite{db: lite}

	case "postgres":
		var pg *sql.DB
		db = &dbPostgres{db: pg}

	}
	confObj.Mu.RUnlock()

	if err != nil {
		log.Fatalf("%v\n", err.Error())
	}

	dbChan <- db

	pullDB()
}

func dbTimer() bool {
	confObj.Mu.RLock()
	answer := time.Since(confObj.LastPush) > confObj.DBInterval
	confObj.Mu.RUnlock()

	return answer
}

// Pushes the registry's cache data to a local
// database for safe keeping.
func pushDB() error {
	db := <-dbChan
	err := db.push()
	dbChan <- db

	return err
}

func pullDB() {
	db := <-dbChan
	db.pull()
	dbChan <- db
}

func (lvl dbLevel) push() error {
	twtxtCache.Mu.RLock()
	var dbBasket = &leveldb.Batch{}
	for k, v := range twtxtCache.Users {
		dbBasket.Put([]byte(k+"*Nick"), []byte(v.Nick))
		dbBasket.Put([]byte(k+"*URL"), []byte(v.URL))
		dbBasket.Put([]byte(k+"*IP"), []byte(v.IP.String()))
		dbBasket.Put([]byte(k+"*Date"), []byte(v.Date))
		for i, e := range v.Status {
			rfc := i.Format(time.RFC3339)
			dbBasket.Put([]byte(k+"*Status*"+rfc), []byte(e))
		}
	}
	twtxtCache.Mu.RUnlock()

	remoteRegistries.Mu.RLock()
	for k, v := range remoteRegistries.List {
		dbBasket.Put([]byte("remote*"+string(k)), []byte(v))
	}
	remoteRegistries.Mu.RUnlock()

	if err := lvl.db.Write(dbBasket, nil); err != nil {
		return err
	}

	confObj.Mu.Lock()
	confObj.LastPush = time.Now()
	confObj.Mu.Unlock()

	return nil
}

func (lvl dbLevel) pull() {

	iter := lvl.db.NewIterator(nil, nil)

	for iter.Next() {
		key := string(iter.Key())
		val := string(iter.Value())

		split := strings.Split(key, "*")
		urls := split[0]
		field := split[1]

		if urls == "remote" {
			remoteRegistries.Mu.Lock()
			remoteRegistries.List = append(remoteRegistries.List, val)
			remoteRegistries.Mu.Unlock()
			continue
		}

		data := registry.NewUser()
		twtxtCache.Mu.RLock()
		if _, ok := twtxtCache.Users[urls]; ok {
			data = twtxtCache.Users[urls]
		}
		twtxtCache.Mu.RUnlock()

		switch field {
		case "IP":
			data.IP = net.ParseIP(val)
		case "Nick":
			data.Nick = val
		case "URL":
			data.URL = val
		case "Date":
			data.Date = val
		case "Status":
			thetime, err := time.Parse(time.RFC3339, split[2])
			if err != nil {
				log.Printf("%v\n", err.Error())
			}
			data.Status[thetime] = val
		}

		twtxtCache.Mu.Lock()
		twtxtCache.Users[urls] = data
		twtxtCache.Mu.Unlock()

	}

	remoteRegistries.Mu.Lock()
	remoteRegistries.List = dedupe(remoteRegistries.List)
	remoteRegistries.Mu.Unlock()

	iter.Release()
	err := iter.Error()
	if err != nil {
		log.Printf("Error while pulling DB into registry cache: %v\n", err.Error())
	}
}

func (lite dbSqlite) push() error {

	return nil
}

func (lite dbSqlite) pull() {

}

func (pg dbPostgres) push() error {

	return nil
}

func (pg dbPostgres) pull() {

}