Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
cs733
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Sushant Mahajan
cs733
Commits
a0cf72e5
Commit
a0cf72e5
authored
Jan 25, 2015
by
Sushant Mahajan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
modified server to accept any kind to value string including CR, LF etc
parent
8f6dc2ca
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
80 additions
and
30 deletions
+80
-30
assignment1/server.go
assignment1/server.go
+80
-30
No files found.
assignment1/server.go
View file @
a0cf72e5
...
...
@@ -35,6 +35,10 @@ const (
ERR_NOT_FOUND
=
"ERR_NOT_FOUND"
ERR_VERSION
=
"ERR_VERSION"
ERR_INTERNAL
=
"ERR_INTERNAL"
//constant
MAX_CMD_ARGS
=
6
READ_TIMEOUT
=
5
)
//represents the value in the main hashtable (key, value) pair
...
...
@@ -88,13 +92,14 @@ func startServer() {
*arguments: channel shared between this go routine and other functions performing actions based on the commands given, client connection
*return: none
*/
func
myRead
(
ch
chan
string
,
conn
net
.
Conn
)
{
func
myRead
(
ch
chan
[]
byte
,
conn
net
.
Conn
)
{
scanner
:=
bufio
.
NewScanner
(
conn
)
scanner
.
Split
(
CustomSplitter
)
for
{
if
ok
:=
scanner
.
Scan
();
!
ok
{
break
}
else
{
temp
:=
scanner
.
Text
()
temp
:=
scanner
.
Bytes
()
ch
<-
temp
logger
.
Println
(
temp
,
"$$"
)
}
...
...
@@ -119,12 +124,12 @@ func write(conn net.Conn, msg string) {
func
handleClient
(
conn
net
.
Conn
,
table
*
KeyValueStore
)
{
defer
conn
.
Close
()
//channel for every connection for every client
ch
:=
make
(
chan
string
)
ch
:=
make
(
chan
[]
byte
)
go
myRead
(
ch
,
conn
)
for
{
msg
:=
<-
ch
logger
.
Println
(
"Channel: "
,
msg
)
logger
.
Println
(
"Channel: "
,
msg
,
string
(
msg
)
)
if
len
(
msg
)
==
0
{
continue
}
...
...
@@ -231,16 +236,16 @@ func isValid(cmd string, tokens []string, conn net.Conn) int {
*arguments: client connection, message from client, pointer to hashtable structure, channel shared with myRead function
*return: none
*/
func
parseInput
(
conn
net
.
Conn
,
msg
string
,
table
*
KeyValueStore
,
ch
chan
string
)
{
func
parseInput
(
conn
net
.
Conn
,
msg
string
,
table
*
KeyValueStore
,
ch
chan
[]
byte
)
{
tokens
:=
strings
.
Fields
(
msg
)
//general error, don't check for commands, avoid the pain ;)
if
len
(
tokens
)
>
6
{
if
len
(
tokens
)
>
MAX_CMD_ARGS
{
write
(
conn
,
ERR_CMD_ERR
)
return
}
var
buffer
bytes
.
Buffer
//for efficient string concatenation
//logger.Println(tokens)
//for efficient string concatenation
var
buffer
bytes
.
Buffer
switch
tokens
[
0
]
{
case
SET
:
if
isValid
(
SET
,
tokens
,
conn
)
!=
0
{
...
...
@@ -362,30 +367,29 @@ func parseInput(conn net.Conn, msg string, table *KeyValueStore, ch chan string)
*parameters: channel to read data from, threshold number of bytes to read
*returns: the value string and error state
*/
func
readValue
(
ch
chan
string
,
n
uint64
)
([]
byte
,
bool
)
{
func
readValue
(
ch
chan
[]
byte
,
n
uint64
)
([]
byte
,
bool
)
{
//now we need to read the value which should have been sent
valReadLength
:=
uint64
(
0
)
var
v
string
var
v
[]
byte
err
:=
false
up
:=
make
(
chan
bool
,
1
)
//after 5 seconds passed reading value, we'll just send err to client
go
func
()
{
time
.
Sleep
(
5
*
time
.
Second
)
time
.
Sleep
(
READ_TIMEOUT
*
time
.
Second
)
up
<-
true
}()
//use select for the data channel and the timeout channel
for
valReadLength
<
n
{
for
valReadLength
<
n
+
2
{
select
{
case
temp
:=
<-
ch
:
logger
.
Println
(
"Value chunk read!"
)
valReadLength
+=
uint64
(
len
(
temp
))
v
+=
temp
v
=
append
(
v
,
temp
...
)
case
<-
up
:
err
=
true
logger
.
Println
(
"Oh, Oh timeout"
)
//write(conn, ERR_INTERNAL)
break
}
...
...
@@ -398,17 +402,19 @@ func readValue(ch chan string, n uint64) ([]byte, bool) {
if
err
{
return
[]
byte
{
0
},
err
}
return
[]
byte
(
v
)
,
err
return
v
[
:
n
]
,
err
}
/*Delegate function responsible for all parsing and hashtable interactions for the SET command sent by client
*arguments: client connection, tokenized command sent by the client, pointer to hashtable structure, channel shared with myRead
*return: version of inserted key (if successful, 0 otherwise), success or failure, whether to send reply to client
*/
func
performSet
(
conn
net
.
Conn
,
tokens
[]
string
,
table
*
KeyValueStore
,
ch
chan
string
)
(
uint64
,
bool
,
bool
)
{
func
performSet
(
conn
net
.
Conn
,
tokens
[]
string
,
table
*
KeyValueStore
,
ch
chan
[]
byte
)
(
uint64
,
bool
,
bool
)
{
k
:=
tokens
[
0
]
e
,
_
:=
strconv
.
ParseUint
(
tokens
[
1
],
10
,
64
)
//expiry time offset
n
,
_
:=
strconv
.
ParseUint
(
tokens
[
2
],
10
,
64
)
//numbytes
//expiry time offset
e
,
_
:=
strconv
.
ParseUint
(
tokens
[
1
],
10
,
64
)
//numbytes
n
,
_
:=
strconv
.
ParseUint
(
tokens
[
2
],
10
,
64
)
r
:=
true
if
len
(
tokens
)
==
4
&&
tokens
[
3
]
==
NOREPLY
{
...
...
@@ -452,11 +458,14 @@ func performSet(conn net.Conn, tokens []string, table *KeyValueStore, ch chan st
*/
func
performGet
(
conn
net
.
Conn
,
tokens
[]
string
,
table
*
KeyValueStore
)
(
*
Data
,
bool
)
{
k
:=
tokens
[
0
]
defer
table
.
RUnlock
()
table
.
RLock
()
defer
table
.
Unlock
()
//lock because if key is expired, we'll delete it
table
.
Lock
()
//critical section begin
if
v
,
ok
:=
table
.
dictionary
[
k
];
ok
{
if
!
v
.
isPerpetual
&&
v
.
expTime
<
uint64
(
time
.
Now
()
.
Unix
())
{
//delete the key
delete
(
table
.
dictionary
,
k
)
return
nil
,
false
}
data
:=
new
(
Data
)
...
...
@@ -474,11 +483,13 @@ func performGet(conn net.Conn, tokens []string, table *KeyValueStore) (*Data, bo
*/
func
performGetm
(
conn
net
.
Conn
,
tokens
[]
string
,
table
*
KeyValueStore
)
(
*
Data
,
bool
)
{
k
:=
tokens
[
0
]
defer
table
.
R
Unlock
()
table
.
R
Lock
()
defer
table
.
Unlock
()
table
.
Lock
()
//critical section begin
if
v
,
ok
:=
table
.
dictionary
[
k
];
ok
{
if
!
v
.
isPerpetual
&&
v
.
expTime
<
uint64
(
time
.
Now
()
.
Unix
())
{
//delete the key
delete
(
table
.
dictionary
,
k
)
return
nil
,
false
}
data
:=
new
(
Data
)
...
...
@@ -498,7 +509,7 @@ func performGetm(conn net.Conn, tokens []string, table *KeyValueStore) (*Data, b
*return: new version of updated key (if it is updated), error status {0: error while reading new value, 1: key found and changed,
*2: version mismatch with key, 3: key not found}, whether to reply to client
*/
func
performCas
(
conn
net
.
Conn
,
tokens
[]
string
,
table
*
KeyValueStore
,
ch
chan
string
)
(
uint64
,
int
,
bool
)
{
func
performCas
(
conn
net
.
Conn
,
tokens
[]
string
,
table
*
KeyValueStore
,
ch
chan
[]
byte
)
(
uint64
,
int
,
bool
)
{
k
:=
tokens
[
0
]
e
,
_
:=
strconv
.
ParseUint
(
tokens
[
1
],
10
,
64
)
ve
,
_
:=
strconv
.
ParseUint
(
tokens
[
2
],
10
,
64
)
...
...
@@ -519,7 +530,8 @@ func performCas(conn net.Conn, tokens []string, table *KeyValueStore, ch chan st
if
val
,
ok
:=
table
.
dictionary
[
k
];
ok
{
if
val
.
version
==
ve
{
if
val
.
isPerpetual
||
val
.
expTime
>=
uint64
(
time
.
Now
()
.
Unix
())
{
if
e
==
0
{
//if expiry time is zero, key should not be deleted
//if expiry time is zero, key should not be deleted
if
e
==
0
{
val
.
isPerpetual
=
true
val
.
expTime
=
0
}
else
{
...
...
@@ -530,7 +542,8 @@ func performCas(conn net.Conn, tokens []string, table *KeyValueStore, ch chan st
ver
++
val
.
version
=
ver
val
.
value
=
v
return
val
.
version
,
0
,
r
//key found and changed
//key found and changed
return
val
.
version
,
0
,
r
}
else
{
logger
.
Println
(
"expired key found!"
)
//version found but key expired, can delete key safely and tell client that it does not exist
...
...
@@ -538,9 +551,11 @@ func performCas(conn net.Conn, tokens []string, table *KeyValueStore, ch chan st
return
0
,
3
,
r
}
}
return
0
,
2
,
r
//version mismatch
//version mismatch
return
0
,
2
,
r
}
return
0
,
3
,
r
//key not found
//key not found
return
0
,
3
,
r
}
}
...
...
@@ -557,12 +572,15 @@ func performDelete(conn net.Conn, tokens []string, table *KeyValueStore) int {
//begin critical section
if
v
,
ok
:=
table
.
dictionary
[
k
];
ok
{
if
v
.
isPerpetual
||
v
.
expTime
>=
uint64
(
time
.
Now
()
.
Unix
())
{
flag
=
0
//found not expired
//found not expired
flag
=
0
}
delete
(
table
.
dictionary
,
k
)
//delete anyway as expired or needs to be deleted
//delete anyway as expired or needs to be deleted
delete
(
table
.
dictionary
,
k
)
return
flag
}
return
2
//key not found
//key not found
return
2
}
/*Simple function that dumps the contents of the hashtable
...
...
@@ -577,6 +595,38 @@ func debug(table *KeyValueStore) {
logger
.
Println
(
"----end debug----"
)
}
/*Copied from the bufio.Scanner (originally ScanLines). By default it splits by '\n' but now we want it to split by '\r\n'
*arguments: data in bytes, is eof reached
*return: next sequence of bytes, chunk of data found, err state
*/
func
CustomSplitter
(
data
[]
byte
,
atEOF
bool
)
(
advance
int
,
token
[]
byte
,
err
error
)
{
omega
:=
0
if
atEOF
&&
len
(
data
)
==
0
{
return
0
,
nil
,
nil
}
for
{
if
i
:=
bytes
.
IndexByte
(
data
[
omega
:
],
'\n'
);
i
>
0
{
//here we add omega as we are using the complete data array instead of the slice where we found '\n'
if
data
[
omega
+
i
-
1
]
==
'\r'
{
//next byte begins at i+1 and data[0:i+1] returned
return
i
+
1
,
data
[
0
:
i
+
1
],
nil
}
else
{
//move the omega index to the byte after \n
omega
=
i
+
1
}
}
else
{
//need to break free the chains
break
}
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if
atEOF
{
return
len
(
data
),
data
,
nil
}
// Request more data.
return
0
,
nil
,
nil
}
/*Entry point of this program. Initializes the start of ther server and sets up the logger.
*arguments: none
*return: none
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment