[
  {
    "path": ".github/workflows/go.yaml",
    "content": "# This workflow will build a golang project\n# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go\n\nname: GoCoverage # The name of the workflow that will appear on Github\n\non:\n  push:\n    branches: [ \"main\" ]\n  pull_request:\n    branches: [ \"main\" ]\n\njobs:\n\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v3\n\n    - name: Set up Go\n      uses: actions/setup-go@v4\n      with:\n        go-version: '1.20'\n\n    - name: Build\n      run: go build -v ./...\n\n    - name: Test\n      run: go test -coverprofile=\"coverage.txt\" -v ./...\n    \n    - name: Upload coverage reports to Codecov\n      uses: codecov/codecov-action@v3\n      env:\n        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with `go test -c`\n*.test\n\n# Output of the go coverage tool\n*.out\nPR\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Constantine Zavezeon <kwynto@mail.ru>\n\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "# GoSession\nThis is quick session for net/http in GoLang.  \nThis package is perhaps the best implementation of the session mechanism, at least it tries to become one.  \n\n[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)\n[![GoDoc](https://godoc.org/github.com/Kwynto/gosession?status.svg)](https://godoc.org/github.com/Kwynto/gosession)\n[![Go Report Card](https://goreportcard.com/badge/github.com/Kwynto/gosession)](https://goreportcard.com/report/github.com/Kwynto/gosession)\n[![GitHub](https://img.shields.io/github/license/Kwynto/gosession)](https://github.com/Kwynto/gosession/blob/master/LICENSE)\n[![codecov](https://codecov.io/gh/Kwynto/gosession/branch/main/graph/badge.svg?token=TXP2NOMK58)](https://codecov.io/gh/Kwynto/gosession) \n\n**Important note**\nThis package is designed to work with the standard net/http package and has not been tested with other http packages by the developer.\n\n## Contents\n\n- [GoSession](#gosession)\n  - [Contents](#contents)\n  - [What are sessions and why are they needed](#what-are-sessions-and-why-are-they-needed)\n  - [How to connect GoSession](#how-to-connect-gosession)\n  - [How to use GoSession](#how-to-use-gosession)\n  - [Examples of using](#examples-of-using)\n    - [Example 1:](#example-1)\n    - [Example 2:](#example-2)\n    - [Example 3:](#example-3)\n  - [About the package](#about-the-package)\n  - [About the author](#about-the-author)\n\n## What are sessions and why are they needed\nA session on a site is a good method of identifying a site user.  \nA session is often used to authorize a user and retain their identity until the user closes the browser page.  \nWhile the user is working with the site, he saves cookies with a unique identifier, by this identifier one can distinguish one user from another and the server can store special data for a particular user.  \nUser data received during the session period can be used for authorization, marketing and many other cases when it is necessary to collect, process and analyze data about a specific user.  \nA session is an efficient method of interacting with a user.\n\n**[⬆ back to top](#gosession)** - **[⬆ back to the chapter](#what-are-sessions-and-why-are-they-needed)**\n\n## How to connect GoSession\nIn your project folder, initialize the Go-module with the command\n> go mod init your_app_name\n\nDownload and install GoSession\n> go get github.com/Kwynto/gosession\n\nNow you can add the GoSession package to your Go-code file, for example in `main.go`\n```go\nimport \"github.com/Kwynto/gosession\"\n```\n\n**[⬆ back to top](#gosession)** - **[⬆ back to the chapter](#how-to-connect-gosession)**\n\n## How to use GoSession\nTo use the GoSession package, you need to import it into your code.\n```go\nimport \"github.com/Kwynto/gosession\"\n```\n\nAll operations for working with sessions must be called from handlers.  \nEach time you start working with the session store, you need to call `gosession.Start(w *http.ResponseWriter, r *http.Request)`, since this function returns the identifier of the store and allows you to access the elements of the store through the identifier.\n```go\nid := gosession.Start(&w, r)\n```\n\nYou need to call the `gosession.Start(w *http.ResponseWriter, r *http.Request)` function from the handler\n```go\nfunc rootHandler(w http.ResponseWriter, r *http.Request) {\n  id := gosession.Start(&w, r) // Get the storage ID for a specific user\n\n  html := \"<html><head><title>Title</title></head><body>%s</body></html>\"\n  fmt.Fprintf(w, html, id)\n}\n```\n\nAlternatively, you can use the `gosession.StartSecure(w *http.ResponseWriter, r *http.Request)` function instead of `gosession.Start(w, r)`.  \nThe `StartSecure()` function replaces the session ID each time it is accessed, which reduces the possibility of ID hijacking.  \nThe use of these functions is exactly the same.  \n```go\nid := gosession.StartSecure(&w, r)\n```\n\nYou need to call the `gosession.StartSecure()` function from the handler  \n```go\nfunc rootHandler(w http.ResponseWriter, r *http.Request) {\n  id := gosession.StartSecure(&w, r) // Get the storage ID for a specific user\n\n  html := \"<html><head><title>Title</title></head><body>%s</body></html>\"\n  fmt.Fprintf(w, html, id)\n}\n```\n\nOnce you have a store ID, you can write variables to the store, read them, and delete them.\n\nRecording is done using the `Set(name string, value interface{})` method\n```go\nid.Set(\"name variable\", anyVariable)\n```\n\nIn the handler it looks like this\n```go\nfunc writeHandler(w http.ResponseWriter, r *http.Request) {\n  name := \"username\"\n  username := \"JohnDow\"\n\n  id := gosession.Start(&w, r)\n  id.Set(name, username)\n\n  html := \"<html><head><title>Title</title></head><body>OK</body></html>\"\n  fmt.Fprint(w, html)\n}\n```\n\nReading is done by `Get(name string) interface{}` method for one variable  \nand the `GetAll() Session` method to read all session variables\n```go\nanyVariable := id.Get(\"name variable\")\n```\n\n```go\nallVariables := id.GetAll()\n```\n\nIn the handler it looks like this\n```go\nfunc readHandler(w http.ResponseWriter, r *http.Request) {\n  name := \"username\"\n  var username interface{}\n\n  id := gosession.Start(&w, r)\n  username = id.Get(name) // Reading the \"username\" variable from the session for a specific user\n\n  html := \"<html><head><title>Title</title></head><body>%s</body></html>\"\n  fmt.Fprintf(w, html, username)\n}\n```\n\nor so\n```go\nfunc readHandler(w http.ResponseWriter, r *http.Request) {\n  var tempStr string = \"\"\n\n  id := gosession.Start(&w, r)\n  allVariables := id.GetAll() // Reading the entire session for a specific client\n\n  for i, v := range allVariables {\n    tempStr = fmt.Sprint(tempStr, i, \"=\", v, \"<br>\")\n  }\n  html := \"<html><head><title>Title</title></head><body>%s</body></html>\"\n  fmt.Fprintf(w, html, tempStr)\n}\n```\n\nRemoving an entry from a session of a specific client is carried out using the `Remove(name string)` method\n```go\nid.Remove(\"name variable\")\n```\n\nIn the handler it looks like this\n```go\nfunc removeHandler(w http.ResponseWriter, r *http.Request) {\n  id := gosession.Start(&w, r)\n  id.Remove(\"name variable\") // Removing a variable from a specific client session\n\n  html := \"<html><head><title>Title</title></head><body>OK</body></html>\"\n  fmt.Fprint(w, html)\n}\n```\n\nRemoving the entire session of a specific client is done using the `Destroy(w *http.ResponseWriter)` method\n```go\nid.Destroy(&w)\n```\n\nIn the handler it looks like this\n```go\nfunc destroyHandler(w http.ResponseWriter, r *http.Request) {\n  id := gosession.Start(&w, r)\n  id.Destroy(&w) // Deleting the entire session of a specific client\n\n  html := \"<html><head><title>Title</title></head><body>OK</body></html>\"\n  fmt.Fprint(w, html)\n}\n```\n\nGoSession allows you to change its settings with the `SetSettings(setings GoSessionSetings)` function,  \nwhich is used outside of the handler, for example, inside the `main()` function\n```go\nvar mySetingsSession = gosession.GoSessionSetings{\n  CookieName:    gosession.GOSESSION_COOKIE_NAME,\n  Expiration:    gosession.GOSESSION_EXPIRATION,\n  TimerCleaning: gosession.GOSESSION_TIMER_FOR_CLEANING,\n}\n\ngosession.SetSetings(mySetingsSession) // Setting session preferences\n```\n\nGoSession has 3 constants available for use\n```go\nconst (\n  GOSESSION_COOKIE_NAME        string        = \"SessionId\" // Name for session cookies\n  GOSESSION_EXPIRATION         int64         = 43_200      // Max age is 12 hours.\n  GOSESSION_TIMER_FOR_CLEANING time.Duration = time.Hour   // The period of launch of the mechanism of cleaning from obsolete sessions\n)\n```\n\nThe remaining functions, types and variables in GoSession are auxiliary and are used only within the package.\n\n**[⬆ back to top](#gosession)** - **[⬆ back to the chapter](#how-to-use-gosession)**\n\n## Examples of using\n\n### Example 1:\n*This is a simple authorization example and it shows the use of the write and read session variables functions, as well as deleting the entire session.*\n\nDownload the GoSession project to your computer:\n> git clone https://github.com/Kwynto/gosession.git\n\nGo to the example folder or open this folder in your IDE.\n> cd ./gosession/pkg/example1\n\nInstall GoSession\n> go get github.com/Kwynto/gosession\n\nRun:\n> go mod tidy\n\nStart the server:\n> go run .\n\nVisit site\n> http://localhost:8080/\n\n### Example 2:\n*This example shows a primitive way to collect information about user actions. You can collect any public user data, as well as track user actions, and then save and process this data.*\n\nDownload the GoSession project to your computer:\n> git clone https://github.com/Kwynto/gosession.git\n\nGo to the example folder or open this folder in your IDE.\n> cd ./gosession/pkg/example2\n\nInstall GoSession\n> go get github.com/Kwynto/gosession\n\nRun:\n> go mod tidy\n\nStart the server:\n> go run .\n\nVisit site\n> http://localhost:8080/\n\nNow you can follow the links on this site and see how the site saves and shows your browsing history.\n\n### Example 3:\n*This example shows a simple, realistic site that uses the session mechanism.*\n\nDownload the GoSession project to your computer:\n> git clone https://github.com/Kwynto/gosession.git\n\nGo to the example folder or open this folder in your IDE.\n> cd ./gosession/pkg/example3\n\nInstall GoSession\n> go get github.com/Kwynto/gosession\n\nRun:\n> go mod tidy\n\nStart the server:\n> go run ./cmd/web/\n\nVisit site\n> http://localhost:8080/\n\nNow you can follow the links on this site.\n\n**[⬆ back to top](#gosession)** - **[⬆ back to the chapter](#examples-of-using)**\n\n## About the package\n\nGoSession has a description of its functionality in a `README.md` file and internal documentation.  \nGoSession is tested and has a performance check.  \nYou can use the GoSession tests and documentation yourself.\n\nDownload the GoSession project to your computer:\n> git clone https://github.com/Kwynto/gosession.git\n\nGo to the project folder:\n> cd ./gosession\n\n**Check out the documentation**\n\nLook at the documentation in two steps.  \nFirst, in the console, run:\n> godoc -http=:8080\n\nAnd then in your web browser navigate to the uri:\n> http://localhost:8080\n\n*The `godoc` utility may not be present in your Go build and you may need to install it  \ncommand `go get -v golang.org/x/tools/cmd/godoc`*\n\n**For Debian Linux users (Ubuntu, Mint and others):** *You may need to install the tools with the `sudo apt install golang-golang-x-tools` command* \n\nYou can also use Go's standard functionality to view documentation in the console via `go doc`.  \nFor example:  \n> go doc Start\n\nIf your IDE is good enough, then the documentation for functions and methods will be available from your code editor.\n\n**Testing**\n\nRun tests:\n> go test -v\n\nRun tests showing code coverage:\n> go test -cover -v\n\nYou can view code coverage in detail in your web browser.  \nTo do this, you need to sequentially execute two commands in the console:\n> go test -coverprofile=\"coverage.out\" -v  \n> go tool cover -html=\"coverage.out\"\n\n**Performance**\n\nYou can look at code performance tests:\n> go test -benchmem -bench=\".\" gosession.go gosession_test.go\n\n*The slowest of all functions is `cleaningSessions()`, but this should not scare you, as it is a utility function and is rarely executed. This function does not affect the performance of the entire mechanism, it is only needed to clean up the storage from lost sessions.*\n\n**[⬆ back to top](#gosession)** - **[⬆ back to the chapter](#about-the-package)**\n\n## Support the author\n\nYou can support open source projects and the author of this project. The details are [here](https://github.com/Kwynto/Kwynto/blob/main/SUPPORT.md).  \n\n## About the author\n\nThe author of the project is Constantine Zavezeon (Kwynto).  \nYou can contact the author by e-mail: kwynto@mail.ru  \nThe author accepts proposals for participation in open source projects,  \nas well as willing to accept job offers.\nIf you want to offer me a job, then first I ask you to read [this](https://github.com/Kwynto/Kwynto/blob/main/offer.md).\n\n**[⬆ back to top](#gosession)**"
  },
  {
    "path": "go.mod",
    "content": "module github.com/Kwynto/gosession\n\ngo 1.17\n"
  },
  {
    "path": "gosession.go",
    "content": "// This is quick session for net/http in golang.\n// This package is perhaps the best implementation of the session mechanism, at least it tries to become one.\npackage gosession\n\n// --------------------------------------------------------\n// Copyright (c) 2022 Constantine Zavezeon <kwynto@mail.ru>\n// --------------------------------------------------------\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync\"\n\t\"time\"\n)\n\nconst (\n\tGOSESSION_COOKIE_NAME        string        = \"SessionId\" // Name for session cookies\n\tGOSESSION_EXPIRATION         int64         = 43_200      // Max age is 12 hours.\n\tGOSESSION_TIMER_FOR_CLEANING time.Duration = time.Hour   // The period of launch of the mechanism of cleaning from obsolete sessions\n)\n\n// The SessionId type is the session identifier\ntype SessionId string\n\n// The Session type contains variables defined for session storage for each client.\ntype Session map[string]interface{}\n\n// The internalSession type is the internal server representation of the session\ntype internalSession struct {\n\texpiration int64\n\tdata       Session\n}\n\n// The serverSessions type is intended to describe all sessions of all client connections\ntype serverSessions map[SessionId]internalSession\n\n// The GoSessionSetings type describes the settings for the session system\ntype GoSessionSetings struct {\n\tCookieName    string\n\tExpiration    int64\n\tTimerCleaning time.Duration\n}\n\n// The allSessions variable stores all sessions of all clients\nvar allSessions serverSessions = make(serverSessions, 0)\n\n// Session mechanism settings variable\nvar setingsSession = GoSessionSetings{\n\tCookieName:    GOSESSION_COOKIE_NAME,\n\tExpiration:    GOSESSION_EXPIRATION,\n\tTimerCleaning: GOSESSION_TIMER_FOR_CLEANING,\n}\n\nvar block sync.RWMutex\n\n// The generateId() generates a new session id in a random, cryptographically secure manner\nfunc generateId() SessionId {\n\tb := make([]byte, 32)\n\trand.Read(b)\n\treturn SessionId(fmt.Sprintf(\"%x\", b))\n}\n\n// The getOrSetCookie(w, r) gets the session id from the cookie, or creates a new one if it can't get\nfunc getOrSetCookie(w *http.ResponseWriter, r *http.Request) SessionId {\n\tdata, err := r.Cookie(setingsSession.CookieName)\n\tif err != nil {\n\t\tid := generateId()\n\t\tcookie := &http.Cookie{\n\t\t\tName:   setingsSession.CookieName,\n\t\t\tValue:  string(id),\n\t\t\tMaxAge: 0,\n\t\t}\n\t\thttp.SetCookie(*w, cookie)\n\t\treturn id\n\t}\n\treturn SessionId(data.Value)\n}\n\n// The deleteCookie(w) function deletes the session cookie\nfunc deleteCookie(w *http.ResponseWriter) {\n\tcookie := &http.Cookie{\n\t\tName:   setingsSession.CookieName,\n\t\tValue:  \"\",\n\t\tMaxAge: -1,\n\t}\n\thttp.SetCookie(*w, cookie)\n}\n\n// The cleaningSessions() function periodically cleans up the server's session storage\nfunc cleaningSessions() {\n\tpresently := time.Now().Unix()\n\tblock.Lock()\n\tfor id, ses := range allSessions {\n\t\tif ses.expiration < presently {\n\t\t\tdelete(allSessions, id)\n\t\t}\n\t}\n\tblock.Unlock()\n\t// log.Println(\"Session storage has been serviced.\")\n\ttime.AfterFunc(setingsSession.TimerCleaning, cleaningSessions)\n}\n\n// The writeS() method safely writes data to the session store\nfunc (id SessionId) writeS(iSes internalSession) {\n\tblock.Lock()\n\tallSessions[id] = iSes\n\tblock.Unlock()\n}\n\n// The readS() method safely reads data from the session store.\nfunc (id SessionId) readS() (internalSession, bool) {\n\tblock.RLock()\n\tdefer block.RUnlock()\n\tses, ok := allSessions[id]\n\tif !ok {\n\t\treturn internalSession{}, false\n\t}\n\treturn ses, true\n}\n\n// The destroyS() method safely deletes the entire session from the store.\nfunc (id SessionId) destroyS() {\n\tblock.Lock()\n\tdelete(allSessions, id)\n\tblock.Unlock()\n}\n\n// The deleteS() method safely deletes one client variable from the session by its name\n// name - session variable name\nfunc (id SessionId) deleteS(name string) {\n\tblock.Lock()\n\tses, ok := allSessions[id]\n\tif ok {\n\t\tdelete(ses.data, name)\n\t\tallSessions[id] = ses\n\t}\n\tblock.Unlock()\n}\n\n// The Set(name, value) SessionId-method to set the client variable to be stored in the session system.\n// name - session variable name.\n// value - directly variable in session.\nfunc (id SessionId) Set(name string, value interface{}) {\n\tses, ok := id.readS()\n\tif ok {\n\t\tses.data[name] = value\n\t\tid.writeS(ses)\n\t}\n}\n\n// The GetAll() SessionId-method to get all client variables from the session system\nfunc (id SessionId) GetAll() Session {\n\tses, _ := id.readS()\n\treturn ses.data\n}\n\n// The Get(name) SessionId-method to get a specific client variable from the session system.\n// name - session variable name\nfunc (id SessionId) Get(name string) interface{} {\n\tses, _ := id.readS()\n\treturn ses.data[name]\n}\n\n// The Destroy(w) SessionId-method to remove the entire client session\nfunc (id SessionId) Destroy(w *http.ResponseWriter) {\n\tid.destroyS()\n\tdeleteCookie(w)\n}\n\n// The Remove(name) SessionId-method to remove one client variable from the session by its name\nfunc (id SessionId) Remove(name string) {\n\tid.deleteS(name)\n}\n\n// The SetSetings(settings) sets new settings for the session mechanism.\n// setings - gosession.GoSessionSetings public type variable for setting new session settings\nfunc SetSetings(setings GoSessionSetings) {\n\tsetingsSession = setings\n}\n\n// The Start(w, r) function starts the session and returns the SessionId to the handler for further use of the session mechanism.\n// This function must be run at the very beginning of the http.Handler\nfunc Start(w *http.ResponseWriter, r *http.Request) SessionId {\n\tid := getOrSetCookie(w, r)\n\tses, ok := id.readS()\n\tif !ok {\n\t\tses.data = make(Session, 0)\n\t}\n\tpresently := time.Now().Unix()\n\tses.expiration = presently + setingsSession.Expiration\n\tid.writeS(ses)\n\treturn id\n}\n\n// The StartSecure(w, r) function starts the session or changes the session ID and sets new cookie to the client.\n// This function must be run at the very beginning of the http.Handler\nfunc StartSecure(w *http.ResponseWriter, r *http.Request) SessionId {\n\tid := getOrSetCookie(w, r)\n\tses, ok := id.readS()\n\tif !ok {\n\t\tses.data = make(Session, 0)\n\t\tpresently := time.Now().Unix()\n\t\tses.expiration = presently + setingsSession.Expiration\n\t\tid.writeS(ses)\n\t\treturn id\n\t} else {\n\t\tid.destroyS()\n\t\tid = generateId()\n\t\tcookie := &http.Cookie{\n\t\t\tName:   setingsSession.CookieName,\n\t\t\tValue:  string(id),\n\t\t\tMaxAge: 0,\n\t\t}\n\t\thttp.SetCookie(*w, cookie)\n\t\tpresently := time.Now().Unix()\n\t\tses.expiration = presently + setingsSession.Expiration\n\t\tid.writeS(ses)\n\t\treturn id\n\t}\n}\n\n// Package initialization\nfunc init() {\n\ttime.AfterFunc(setingsSession.TimerCleaning, cleaningSessions)\n\t// log.Println(\"GoSessions initialized\")\n}\n"
  },
  {
    "path": "gosession_test.go",
    "content": "package gosession\n\n// --------------------------------------------------------\n// Copyright (c) 2022 Constantine Zavezeon <kwynto@mail.ru>\n// --------------------------------------------------------\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\t\"time\"\n)\n\nconst (\n\tGOSESSION_TESTING_ITER int = 100\n)\n\n// --------------\n// Test functions\n// --------------\n\nfunc Test_generateId(t *testing.T) {\n\ttestVar := make(map[int]SessionId)\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\ttestVar[i] = generateId() // calling the tested function\n\t}\n\tfor _, v1 := range testVar {\n\t\tcount := 0\n\t\tfor _, v2 := range testVar {\n\t\t\tif v1 == v2 {\n\t\t\t\tcount++\n\t\t\t}\n\t\t\t// if bytes.Equal([]byte(v1), []byte(v2)) {\n\t\t\t// \tcount++\n\t\t\t// }\n\t\t}\n\t\t// work check\n\t\tif count > 1 {\n\t\t\tt.Error(\"Error generating unique identifier.\")\n\t\t}\n\t}\n}\n\nfunc Test_getOrSetCookie(t *testing.T) {\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tvar ctrlId SessionId\n\t\thandler := func(w http.ResponseWriter, r *http.Request) {\n\t\t\tsesid := getOrSetCookie(&w, r) // calling the tested function\n\t\t\tctrlId = sesid\n\t\t\tio.WriteString(w, string(sesid))\n\t\t}\n\t\tw := httptest.NewRecorder()\n\t\tr := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\thandler(w, r)\n\n\t\tstatus := w.Code\n\t\t// work check\n\t\tif status != http.StatusOK {\n\t\t\tt.Errorf(\"Handler returned %v\", status)\n\t\t}\n\n\t\tcookies := w.Result().Cookies()\n\t\tnoErr := false\n\t\tfor _, v := range cookies {\n\t\t\tif v.Name == setingsSession.CookieName && v.Value == string(ctrlId) {\n\t\t\t\tnoErr = true\n\t\t\t}\n\t\t}\n\t\t// work check\n\t\tif !noErr {\n\t\t\tt.Error(\"the server returned an invalid ID\")\n\t\t}\n\t}\n\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tvar ctrlId SessionId\n\t\thandler := func(w http.ResponseWriter, r *http.Request) {\n\t\t\tsesid := getOrSetCookie(&w, r) // calling the tested function\n\t\t\tctrlId = sesid\n\t\t\tio.WriteString(w, string(sesid))\n\t\t}\n\t\tw := httptest.NewRecorder()\n\t\tr := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\tclientId := generateId()\n\t\tcookie := &http.Cookie{\n\t\t\tName:   setingsSession.CookieName,\n\t\t\tValue:  string(clientId),\n\t\t\tMaxAge: 0,\n\t\t}\n\t\tr.AddCookie(cookie)\n\t\thandler(w, r)\n\n\t\tstatus := w.Code\n\t\t// work check\n\t\tif status != http.StatusOK {\n\t\t\tt.Errorf(\"Handler returned %v\", status)\n\t\t}\n\t\t// work check\n\t\tif ctrlId != clientId {\n\t\t\tt.Error(\"server received invalid id\")\n\t\t}\n\t}\n}\n\nfunc Test_deleteCookie(t *testing.T) {\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\t// handler := func(w http.ResponseWriter, r *http.Request) {\n\t\thandler := func(w http.ResponseWriter) {\n\t\t\tdeleteCookie(&w) // calling the tested function\n\t\t\tio.WriteString(w, \"<html><head><title>Title</title></head><body>Body</body></html>\")\n\t\t}\n\t\tw := httptest.NewRecorder()\n\t\t// r := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\tclientId := generateId()\n\t\t// cookie := &http.Cookie{\n\t\t// \tName:   setingsSession.CookieName,\n\t\t// \tValue:  string(clientId),\n\t\t// \tMaxAge: 0,\n\t\t// }\n\t\t// r.AddCookie(cookie)\n\t\thandler(w)\n\n\t\tstatus := w.Code\n\t\t// work check\n\t\tif status != http.StatusOK {\n\t\t\tt.Errorf(\"Handler returned %v\", status)\n\t\t}\n\n\t\tcookies := w.Result().Cookies()\n\t\tnoErr := true\n\t\tfor _, v := range cookies {\n\t\t\tif v.Name == setingsSession.CookieName && v.Value == string(clientId) {\n\t\t\t\tnoErr = false\n\t\t\t}\n\t\t}\n\t\t// work check\n\t\tif !noErr {\n\t\t\tt.Error(\"the server did not delete the session cookie\")\n\t\t}\n\t}\n}\n\nfunc Test_cleaningSessions(t *testing.T) {\n\trand.Seed(time.Now().Unix())\n\tvar falseInd int\n\tvar trueInd int\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tfalseInd = rand.Intn(75)\n\t\ttrueInd = rand.Intn(50) + falseInd\n\n\t\tfor id := range allSessions {\n\t\t\tdelete(allSessions, id)\n\t\t}\n\n\t\tfor fi := 0; fi < falseInd; fi++ {\n\t\t\tallSessions[generateId()] = internalSession{\n\t\t\t\texpiration: 0,\n\t\t\t\tdata:       make(Session),\n\t\t\t}\n\t\t}\n\n\t\tfor ti := 0; ti < trueInd; ti++ {\n\t\t\tallSessions[generateId()] = internalSession{\n\t\t\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\t\t\tdata:       make(Session),\n\t\t\t}\n\t\t}\n\n\t\tcleaningSessions() // calling the tested function\n\t\t// work check\n\t\tif len(allSessions) != trueInd {\n\t\t\tt.Error(\"The number of correct entries does not match.\")\n\t\t}\n\t}\n}\n\nfunc Test_writeS(t *testing.T) {\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tses := internalSession{\n\t\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\t\tdata:       make(Session),\n\t\t}\n\t\tid := generateId()\n\t\tid.writeS(ses)\n\t\tif allSessions[id].expiration != ses.expiration {\n\t\t\tt.Error(\"Writing error. Session is not equal.\")\n\t\t}\n\t}\n}\n\nfunc Test_readS(t *testing.T) {\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tses := internalSession{\n\t\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\t\tdata:       make(Session),\n\t\t}\n\t\tid := generateId()\n\t\tallSessions[id] = ses\n\t\tres, _ := id.readS()\n\t\tif res.expiration != ses.expiration {\n\t\t\tt.Error(\"Reading error. Session is not equal.\")\n\t\t}\n\n\t\tdelete(allSessions, id)\n\t\t_, ok := id.readS()\n\t\tif ok {\n\t\t\tt.Error(\"Reading error. Was reaing wrong session.\")\n\t\t}\n\t}\n}\n\nfunc Test_destroyS(t *testing.T) {\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tses := internalSession{\n\t\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\t\tdata:       make(Session),\n\t\t}\n\t\tid := generateId()\n\t\tid.writeS(ses)\n\t\tid.destroyS()\n\t\t_, ok := id.readS()\n\t\tif ok {\n\t\t\tt.Error(\"Destroy error. Was reading deleted session.\")\n\t\t}\n\t}\n}\n\nfunc Test_deleteS(t *testing.T) {\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tses := internalSession{\n\t\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\t\tdata:       make(Session),\n\t\t}\n\t\tses.data[\"name\"] = \"test string\"\n\t\tid := generateId()\n\t\tid.writeS(ses)\n\t\t// id.destroyS()\n\t\tid.deleteS(\"name\")\n\n\t\t_, ok := allSessions[id].data[\"name\"]\n\t\tif ok {\n\t\t\tt.Error(\"Delete error. Was reading deleted variable.\")\n\t\t}\n\t}\n}\n\nfunc Test_Set(t *testing.T) {\n\tvar value interface{}\n\trand.Seed(time.Now().Unix())\n\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tid := generateId()\n\t\tallSessions[id] = internalSession{\n\t\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\t\tdata:       make(Session),\n\t\t}\n\n\t\tname := \"test variable\"\n\t\tswitch rand.Intn(3) {\n\t\tcase 0:\n\t\t\tvalue = true\n\t\tcase 1:\n\t\t\tvalue = fmt.Sprintf(\"test string %d\", rand.Intn(100))\n\t\tcase 2:\n\t\t\tvalue = rand.Intn(100)\n\t\tcase 3:\n\t\t\tvalue = rand.Float64()\n\t\t}\n\n\t\tid.Set(name, value) // calling the tested function\n\t\t// work check\n\t\tif allSessions[id].data[name] != value {\n\t\t\tt.Error(\"Failed to write variable to session storage.\")\n\t\t}\n\t}\n}\n\nfunc Test_GetAll(t *testing.T) {\n\tvar value interface{}\n\tvar name string\n\trand.Seed(time.Now().Unix())\n\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tid := generateId()\n\t\tdata := make(Session)\n\t\tcount := rand.Intn(20) + 1\n\t\tfor ic := 0; ic < count; ic++ {\n\t\t\tname = fmt.Sprintf(\"test name  %d\", rand.Intn(100))\n\t\t\tswitch rand.Intn(3) {\n\t\t\tcase 0:\n\t\t\t\tvalue = true\n\t\t\tcase 1:\n\t\t\t\tvalue = fmt.Sprintf(\"test string %d\", rand.Intn(100))\n\t\t\tcase 2:\n\t\t\t\tvalue = rand.Intn(100)\n\t\t\tcase 3:\n\t\t\t\tvalue = rand.Float64()\n\t\t\t}\n\t\t\tdata[name] = value\n\t\t}\n\t\tallSessions[id] = internalSession{\n\t\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\t\tdata:       data,\n\t\t}\n\n\t\tses := id.GetAll() // calling the tested function\n\t\t// work check\n\t\tfor iname, v := range ses {\n\t\t\tif v != data[iname] {\n\t\t\t\tt.Error(\"Incorrect data received from session variable storage\")\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc Test_Get(t *testing.T) {\n\tvar value interface{}\n\tvar name string\n\trand.Seed(time.Now().Unix())\n\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tid := generateId()\n\t\tdata := make(Session)\n\n\t\tname = \"test name\"\n\t\tswitch rand.Intn(4) {\n\t\tcase 0:\n\t\t\tvalue = true\n\t\tcase 1:\n\t\t\tvalue = fmt.Sprintf(\"test string %d\", rand.Intn(100))\n\t\tcase 2:\n\t\t\tvalue = rand.Intn(100)\n\t\tcase 3:\n\t\t\tvalue = rand.Float64()\n\t\t}\n\t\tdata[name] = value\n\t\tallSessions[id] = internalSession{\n\t\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\t\tdata:       data,\n\t\t}\n\n\t\tgetedValue := id.Get(name) // calling the tested function\n\t\t// work check\n\t\tif getedValue != value {\n\t\t\tt.Error(\"Incorrect data received from session variable storage\")\n\t\t}\n\t}\n}\n\nfunc Test_Destroy(t *testing.T) {\n\tvar hid SessionId\n\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tw := httptest.NewRecorder()\n\t\tr := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\tid := generateId()\n\t\tcookie := &http.Cookie{\n\t\t\tName:   setingsSession.CookieName,\n\t\t\tValue:  string(id),\n\t\t\tMaxAge: 0,\n\t\t}\n\t\tr.AddCookie(cookie)\n\n\t\tdata := make(Session)\n\t\tname := \"test name\"\n\t\tvalue := \"test value\"\n\t\tdata[name] = value\n\t\tallSessions[id] = internalSession{\n\t\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\t\tdata:       data,\n\t\t}\n\n\t\thandler := func(w http.ResponseWriter, r *http.Request) {\n\t\t\thid = Start(&w, r)\n\t\t\thid.Destroy(&w) // calling the tested function\n\t\t\tio.WriteString(w, \"<html><head><title>Title</title></head><body>Body</body></html>\")\n\t\t}\n\t\thandler(w, r)\n\n\t\tstatus := w.Code\n\t\t// work check\n\t\tif status != http.StatusOK {\n\t\t\tt.Errorf(\"Handler returned status: %v\", status)\n\t\t}\n\n\t\t// work check\n\t\tif id != hid {\n\t\t\tt.Error(\"ID mismatch\")\n\t\t}\n\n\t\tcookies := w.Result().Cookies()\n\t\tnoErr := true\n\t\tfor _, v := range cookies {\n\t\t\tif v.Name == setingsSession.CookieName && v.Value == string(id) {\n\t\t\t\tnoErr = false\n\t\t\t}\n\t\t}\n\t\t// work check\n\t\tif !noErr {\n\t\t\tt.Error(\"The server did not delete the session cookie\")\n\t\t}\n\n\t\t// work check\n\t\tif allSessions[id].data != nil {\n\t\t\tt.Error(\"Session has not been deleted.\")\n\t\t}\n\t}\n}\n\nfunc Test_Remove(t *testing.T) {\n\tvar value interface{}\n\tvar name string\n\trand.Seed(time.Now().Unix())\n\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tid := generateId()\n\t\tdata := make(Session)\n\n\t\tname = \"test name\"\n\t\tswitch rand.Intn(3) {\n\t\tcase 0:\n\t\t\tvalue = true\n\t\tcase 1:\n\t\t\tvalue = fmt.Sprintf(\"test string %d\", rand.Intn(100))\n\t\tcase 2:\n\t\t\tvalue = rand.Intn(100)\n\t\tcase 3:\n\t\t\tvalue = rand.Float64()\n\t\t}\n\t\tdata[name] = value\n\t\tallSessions[id] = internalSession{\n\t\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\t\tdata:       data,\n\t\t}\n\n\t\tid.Remove(name) // calling the tested function\n\t\t// work check\n\t\tif allSessions[id].data[name] == value {\n\t\t\tt.Error(\"Failed to change settings\")\n\t\t}\n\t}\n}\n\nfunc Test_SetSetings(t *testing.T) {\n\tvar test_setingsSession1 = GoSessionSetings{\n\t\tCookieName:    \"test_name\",\n\t\tExpiration:    int64(rand.Intn(86_400)),\n\t\tTimerCleaning: time.Minute,\n\t}\n\tvar test_setingsSession2 = GoSessionSetings{\n\t\tCookieName:    GOSESSION_COOKIE_NAME,\n\t\tExpiration:    GOSESSION_EXPIRATION,\n\t\tTimerCleaning: GOSESSION_TIMER_FOR_CLEANING,\n\t}\n\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tSetSetings(test_setingsSession1) // calling the tested function\n\t\t// work check\n\t\tif test_setingsSession1 != setingsSession {\n\t\t\tt.Error(\"Failed to change settings.\")\n\t\t}\n\t\tSetSetings(test_setingsSession2) // calling the tested function\n\t\t// work check\n\t\tif test_setingsSession2 != setingsSession {\n\t\t\tt.Error(\"Failed to change settings.\")\n\t\t}\n\t}\n}\n\nfunc Test_Start(t *testing.T) {\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tvar id1 SessionId\n\t\tvar id2 SessionId\n\t\thandler1 := func(w http.ResponseWriter, r *http.Request) {\n\t\t\tid1 = Start(&w, r) // calling the tested function\n\t\t\tio.WriteString(w, \"<html><head><title>Title</title></head><body>Body</body></html>\")\n\t\t}\n\t\thandler2 := func(w http.ResponseWriter, r *http.Request) {\n\t\t\tid2 = Start(&w, r) // calling the tested function\n\t\t\tio.WriteString(w, \"<html><head><title>Title</title></head><body>Body</body></html>\")\n\t\t}\n\n\t\tw1 := httptest.NewRecorder()\n\t\tr1 := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\thandler1(w1, r1)\n\n\t\tstatus := w1.Code\n\t\t// work check\n\t\tif status != http.StatusOK {\n\t\t\tt.Errorf(\"Handler returned status: %v\", status)\n\t\t}\n\n\t\tcookies := w1.Result().Cookies()\n\t\tvar cookie *http.Cookie\n\t\tfor _, v := range cookies {\n\t\t\tcookie = v\n\t\t}\n\t\tw2 := httptest.NewRecorder()\n\t\tr2 := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\tr2.AddCookie(cookie)\n\t\thandler2(w2, r2)\n\n\t\tstatus = w2.Code\n\t\t// work check\n\t\tif status != http.StatusOK {\n\t\t\tt.Errorf(\"Handler returned status: %v\", status)\n\t\t}\n\n\t\t// work check\n\t\tif id1 != id2 {\n\t\t\tt.Errorf(\"Server and client IDs are not equal:\\n server: %v\\n client: %v\\n\", id1, id2)\n\t\t}\n\t}\n}\n\nfunc Test_StartSecure(t *testing.T) {\n\tfor i := 0; i < GOSESSION_TESTING_ITER; i++ {\n\t\tvar id1 SessionId\n\t\tvar id2 SessionId\n\t\thandler1 := func(w http.ResponseWriter, r *http.Request) {\n\t\t\tid1 = StartSecure(&w, r) // calling the tested function\n\t\t\tio.WriteString(w, \"<html><head><title>Title</title></head><body>Body</body></html>\")\n\t\t}\n\t\thandler2 := func(w http.ResponseWriter, r *http.Request) {\n\t\t\tid2 = StartSecure(&w, r) // calling the tested function\n\t\t\tio.WriteString(w, \"<html><head><title>Title</title></head><body>Body</body></html>\")\n\t\t}\n\n\t\tw1 := httptest.NewRecorder()\n\t\tr1 := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\thandler1(w1, r1)\n\n\t\tstatus := w1.Code\n\t\t// work check\n\t\tif status != http.StatusOK {\n\t\t\tt.Errorf(\"Handler returned status: %v\", status)\n\t\t}\n\n\t\tcookies := w1.Result().Cookies()\n\t\tvar cookie *http.Cookie\n\t\tfor _, v := range cookies {\n\t\t\tcookie = v\n\t\t}\n\t\tw2 := httptest.NewRecorder()\n\t\tr2 := httptest.NewRequest(\"GET\", \"/\", nil)\n\t\tr2.AddCookie(cookie)\n\t\thandler2(w2, r2)\n\n\t\tstatus = w2.Code\n\t\t// work check\n\t\tif status != http.StatusOK {\n\t\t\tt.Errorf(\"Handler returned status: %v\", status)\n\t\t}\n\n\t\t// work check\n\t\tif id1 == id2 {\n\t\t\tt.Errorf(\"Server and client IDs are equal:\\n server: %v\\n client: %v\\n\", id1, id2)\n\t\t}\n\t}\n}\n\n// ----------------------\n// Functions benchmarking\n// ----------------------\n\nfunc Benchmark_generateId(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tgenerateId() // calling the tested function\n\t}\n}\n\nfunc Benchmark_getOrSetCookie(b *testing.B) {\n\thandler := func(w http.ResponseWriter, r *http.Request) {\n\t\tgetOrSetCookie(&w, r) // calling the tested function\n\t}\n\tr := httptest.NewRequest(\"GET\", \"/\", nil)\n\tw := httptest.NewRecorder()\n\tfor i := 0; i < b.N; i++ {\n\t\thandler(w, r)\n\t}\n}\n\nfunc Benchmark_deleteCookie(b *testing.B) {\n\t// handler := func(w http.ResponseWriter, r *http.Request) {\n\thandler := func(w http.ResponseWriter) {\n\t\tdeleteCookie(&w) // calling the tested function\n\t}\n\tw := httptest.NewRecorder()\n\t// r := httptest.NewRequest(\"GET\", \"/\", nil)\n\t// cookie := &http.Cookie{\n\t// \tName:   setingsSession.CookieName,\n\t// \tValue:  string(generateId()),\n\t// \tMaxAge: 0,\n\t// }\n\t// r.AddCookie(cookie)\n\n\tfor i := 0; i < b.N; i++ {\n\t\thandler(w)\n\t}\n}\n\nfunc Benchmark_cleaningSessions(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tcleaningSessions() // calling the tested function\n\t}\n}\n\nfunc Benchmark_writeS(b *testing.B) {\n\tses := internalSession{\n\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\tdata:       make(Session),\n\t}\n\tses.data[\"name\"] = \"test value\"\n\tid := generateId()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tid.writeS(ses) // calling the tested function\n\t}\n}\n\nfunc Benchmark_readS(b *testing.B) {\n\tses := internalSession{\n\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\tdata:       make(Session),\n\t}\n\tses.data[\"name\"] = \"test value\"\n\tid := generateId()\n\tid.writeS(ses)\n\n\tfor i := 0; i < b.N; i++ {\n\t\tid.readS() // calling the tested function\n\t}\n}\n\nfunc Benchmark_destroyS(b *testing.B) {\n\tses := internalSession{\n\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\tdata:       make(Session),\n\t}\n\tses.data[\"name\"] = \"test value\"\n\tid := generateId()\n\tid.writeS(ses)\n\n\tfor i := 0; i < b.N; i++ {\n\t\tid.destroyS() // calling the tested function\n\t}\n}\n\nfunc Benchmark_deleteS(b *testing.B) {\n\tses := internalSession{\n\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\tdata:       make(Session),\n\t}\n\tses.data[\"name\"] = \"test value\"\n\tid := generateId()\n\tid.writeS(ses)\n\n\tfor i := 0; i < b.N; i++ {\n\t\tid.deleteS(\"name\") // calling the tested function\n\t}\n}\n\nfunc Benchmark_Set(b *testing.B) {\n\trand.Seed(time.Now().Unix())\n\tid := generateId()\n\tdata := make(Session)\n\tallSessions[id] = internalSession{\n\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\tdata:       data,\n\t}\n\tname := fmt.Sprintf(\"BenchName%d\", rand.Intn(100))\n\tvalue := rand.Float64()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tid.Set(name, value) // calling the tested function\n\t}\n}\n\nfunc Benchmark_GetAll(b *testing.B) {\n\trand.Seed(time.Now().Unix())\n\tid := generateId()\n\tdata := make(Session)\n\tname := fmt.Sprintf(\"BenchName%d\", rand.Intn(100))\n\tvalue := rand.Float64()\n\tdata[name] = value\n\tallSessions[id] = internalSession{\n\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\tdata:       data,\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\tid.GetAll() // calling the tested function\n\t}\n}\n\nfunc Benchmark_Get(b *testing.B) {\n\trand.Seed(time.Now().Unix())\n\tid := generateId()\n\tdata := make(Session)\n\tname := fmt.Sprintf(\"BenchName%d\", rand.Intn(100))\n\tvalue := rand.Float64()\n\tdata[name] = value\n\tallSessions[id] = internalSession{\n\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\tdata:       data,\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\tid.Get(name) // calling the tested function\n\t}\n}\n\nfunc Benchmark_Destroy(b *testing.B) {\n\tw := httptest.NewRecorder()\n\tr := httptest.NewRequest(\"GET\", \"/\", nil)\n\tid := generateId()\n\tcookie := &http.Cookie{\n\t\tName:   setingsSession.CookieName,\n\t\tValue:  string(id),\n\t\tMaxAge: 0,\n\t}\n\tr.AddCookie(cookie)\n\n\tdata := make(Session)\n\tname := fmt.Sprintf(\"BenchName%d\", rand.Intn(100))\n\tvalue := rand.Float64()\n\tdata[name] = value\n\tallSessions[id] = internalSession{\n\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\tdata:       data,\n\t}\n\n\t// handler := func(w http.ResponseWriter, r *http.Request) {\n\thandler := func(w http.ResponseWriter) {\n\t\tid.Destroy(&w) // calling the tested function\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\thandler(w)\n\t}\n}\n\nfunc Benchmark_Remove(b *testing.B) {\n\trand.Seed(time.Now().Unix())\n\tid := generateId()\n\tdata := make(Session)\n\tname := fmt.Sprintf(\"BenchName%d\", rand.Intn(100))\n\tvalue := rand.Float64()\n\tdata[name] = value\n\tallSessions[id] = internalSession{\n\t\texpiration: time.Now().Unix() + setingsSession.Expiration,\n\t\tdata:       data,\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\tid.Remove(name) // calling the tested function\n\t}\n}\n\nfunc Benchmark_SetSetings(b *testing.B) {\n\tvar test_setingsSession = GoSessionSetings{\n\t\tCookieName:    GOSESSION_COOKIE_NAME,\n\t\tExpiration:    GOSESSION_EXPIRATION,\n\t\tTimerCleaning: GOSESSION_TIMER_FOR_CLEANING,\n\t}\n\tfor i := 0; i < b.N; i++ {\n\t\tSetSetings(test_setingsSession) // calling the tested function\n\t}\n}\n\nfunc Benchmark_Start(b *testing.B) {\n\thandler := func(w http.ResponseWriter, r *http.Request) {\n\t\tStart(&w, r) // calling the tested function\n\t}\n\tr := httptest.NewRequest(\"GET\", \"/\", nil)\n\tw := httptest.NewRecorder()\n\tfor i := 0; i < b.N; i++ {\n\t\thandler(w, r)\n\t}\n}\n\nfunc Benchmark_StartSecure(b *testing.B) {\n\thandler := func(w http.ResponseWriter, r *http.Request) {\n\t\tStartSecure(&w, r) // calling the tested function\n\t}\n\tr := httptest.NewRequest(\"GET\", \"/\", nil)\n\tw := httptest.NewRecorder()\n\tfor i := 0; i < b.N; i++ {\n\t\thandler(w, r)\n\t}\n}\n"
  },
  {
    "path": "pkg/examples/example1/go.mod",
    "content": "module example1\n\ngo 1.17\n\nrequire github.com/Kwynto/gosession v0.2.4\n"
  },
  {
    "path": "pkg/examples/example1/go.sum",
    "content": "github.com/Kwynto/gosession v0.0.0-20220620140922-d2e7b2c35b8e h1:gNsAZ2trdqTriYD0H1YvhsiMzm8hjM3dd1zK64hEndM=\ngithub.com/Kwynto/gosession v0.0.0-20220620140922-d2e7b2c35b8e/go.mod h1:t9ebCczcC/w08whDLwShIZ59finO9EyRkiQf5B31m90=\ngithub.com/Kwynto/gosession v0.2.1 h1:d0Dz1IBcjQ44nkAWkOz7hYEYrCfkyIjOIMnBhVy/GiE=\ngithub.com/Kwynto/gosession v0.2.1/go.mod h1:t9ebCczcC/w08whDLwShIZ59finO9EyRkiQf5B31m90=\ngithub.com/Kwynto/gosession v0.2.3 h1:8Ag0nXX0cCxrbFOFdCdPI9u46YtEEG/JFx1iHF+PH6I=\ngithub.com/Kwynto/gosession v0.2.3/go.mod h1:t9ebCczcC/w08whDLwShIZ59finO9EyRkiQf5B31m90=\ngithub.com/Kwynto/gosession v0.2.4 h1:SdK/0o1o88El3ALz/kFfZlrCfPBC6b6Czha6S5tb6vc=\ngithub.com/Kwynto/gosession v0.2.4/go.mod h1:t9ebCczcC/w08whDLwShIZ59finO9EyRkiQf5B31m90=\n"
  },
  {
    "path": "pkg/examples/example1/main.go",
    "content": "package main\n\nimport (\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/Kwynto/gosession\"\n)\n\nfunc GetMd5(text string) string {\n\th := md5.New()\n\th.Write([]byte(text))\n\treturn hex.EncodeToString(h.Sum(nil))\n}\n\nfunc homeHandler(w http.ResponseWriter, r *http.Request) {\n\tvar html string = \"\"\n\tid := gosession.Start(&w, r)\n\tusername := id.Get(\"username\")\n\n\tif username != nil {\n\t\thtml = `\n\t\t<html>\n\t\t\t<head>\n\t\t\t\t<title>Title</title>\n\t\t\t</head>\n\t\t\t<body>\n\t\t\t\t<p>\n\t\t\t\tYou are authorized!<br>\n\t\t\t\tYour name is %s.<br>\n\t\t\t\t</p>\n\t\t\t\t<p>\n\t\t\t\t<a href=\"/logout\">Logout?</a>\n\t\t\t\t</p>\n\t\t\t\t<p>\n\t\t\t\t<a href=\"/firstpage\">First Page</a>\n\t\t\t\t</p>\n\t\t\t\t<p>\n\t\t\t\t<a href=\"/secondpage\">Second Page</a>\n\t\t\t\t</p>\n\t\t\t</body>\n\t\t</html>\n\t\t`\n\t\thtml = fmt.Sprintf(html, username)\n\t} else {\n\t\thtml = `\n\t\t<html>\n\t\t\t<head>\n\t\t\t\t<title>Title</title>\n\t\t\t</head>\n\t\t\t<body>\n\t\t\t\t<p>\n\t\t\t\t\t<form action=\"/auth\" method=\"post\" class=\"form-horizontal\">\n\t\t\t\t\t\t<input name=\"login\" type=\"text\" value=\"\" placeholder=\"Login\" required pattern=\"^[a-zA-Z0-9_-]+$\">\n\t\t\t\t\t\t<input name=\"password\" type=\"password\" value=\"\" placeholder=\"Password\" required pattern=\"^[a-zA-Z0-9]+$\">\n\t\t\t\t\t\t<button name=\"signin\" type=\"submit\">Auth button</button>\n\t\t\t\t\t</form>\n\t\t\t\t</p>\n\t\t\t\t<p>\n\t\t\t\t\tThis is a test example of authorization on a web page.<br>\n\t\t\t\t\tPlease enter any text as username and password\n\t\t\t\t</p>\n\t\t\t</body>\n\t\t</html>\n\t\t`\n\t}\n\tfmt.Fprint(w, html)\n}\n\nfunc authHandler(w http.ResponseWriter, r *http.Request) {\n\tusername := r.FormValue(\"login\")\n\tpassword := r.FormValue(\"password\")\n\n\tid := gosession.Start(&w, r)\n\n\tif username != \"\" && password != \"\" {\n\t\tpasHash := GetMd5(password)\n\t\tid.Set(\"username\", username)\n\t\tid.Set(\"hash\", pasHash)\n\t}\n\n\thttp.Redirect(w, r, \"/\", http.StatusSeeOther)\n}\n\nfunc outHandler(w http.ResponseWriter, r *http.Request) {\n\tid := gosession.Start(&w, r)\n\tid.Destroy(&w)\n\n\thttp.Redirect(w, r, \"/\", http.StatusSeeOther)\n}\n\nfunc firstHandler(w http.ResponseWriter, r *http.Request) {\n\tid := gosession.Start(&w, r)\n\tses := id.GetAll()\n\tusername := ses[\"username\"]\n\tpasHash := ses[\"hash\"]\n\n\thtml := `\n\t\t<html>\n\t\t\t<head>\n\t\t\t\t<title>Title</title>\n\t\t\t</head>\n\t\t\t<body>\n\t\t\t\t<p>\n\t\t\t\tYou are authorized!<br>\n\t\t\t\t</p>\n\t\t\t\t<p>\n\t\t\t\tYour name is %s.<br>\n\t\t\t\tHash password is %s <br>\n\t\t\t\t</p>\n\t\t\t\t<p>\n\t\t\t\t<a href=\"/\">Home Page</a>\n\t\t\t\t</p>\n\t\t\t</body>\n\t\t</html>\n\t\t`\n\thtml = fmt.Sprintf(html, username, pasHash)\n\tfmt.Fprint(w, html)\n}\n\nfunc secondHandler(w http.ResponseWriter, r *http.Request) {\n\tid := gosession.Start(&w, r)\n\n\thtml := `\n\t\t<html>\n\t\t\t<head>\n\t\t\t\t<title>Title</title>\n\t\t\t</head>\n\t\t\t<body>\n\t\t\t\t<p>\n\t\t\t\tYou are authorized!<br>\n\t\t\t\t</p>\n\t\t\t\t<p>\n\t\t\t\tYour session ID is: %s.<br>\n\t\t\t\t</p>\n\t\t\t\t<p>\n\t\t\t\t<a href=\"/\">Home Page</a>\n\t\t\t\t</p>\n\t\t\t</body>\n\t\t</html>\n\t\t`\n\thtml = fmt.Sprintf(html, id)\n\tfmt.Fprint(w, html)\n}\n\nfunc main() {\n\tport := \":8080\"\n\n\thttp.HandleFunc(\"/\", homeHandler)\n\thttp.HandleFunc(\"/auth\", authHandler)\n\thttp.HandleFunc(\"/logout\", outHandler)\n\thttp.HandleFunc(\"/firstpage\", firstHandler)\n\thttp.HandleFunc(\"/secondpage\", secondHandler)\n\n\thttp.ListenAndServe(port, nil)\n}\n"
  },
  {
    "path": "pkg/examples/example2/go.mod",
    "content": "module example2\n\ngo 1.17\n\nrequire github.com/Kwynto/gosession v0.2.4\n"
  },
  {
    "path": "pkg/examples/example2/go.sum",
    "content": "github.com/Kwynto/gosession v0.0.0-20220620140922-d2e7b2c35b8e h1:gNsAZ2trdqTriYD0H1YvhsiMzm8hjM3dd1zK64hEndM=\ngithub.com/Kwynto/gosession v0.0.0-20220620140922-d2e7b2c35b8e/go.mod h1:t9ebCczcC/w08whDLwShIZ59finO9EyRkiQf5B31m90=\ngithub.com/Kwynto/gosession v0.2.1 h1:d0Dz1IBcjQ44nkAWkOz7hYEYrCfkyIjOIMnBhVy/GiE=\ngithub.com/Kwynto/gosession v0.2.1/go.mod h1:t9ebCczcC/w08whDLwShIZ59finO9EyRkiQf5B31m90=\ngithub.com/Kwynto/gosession v0.2.3 h1:8Ag0nXX0cCxrbFOFdCdPI9u46YtEEG/JFx1iHF+PH6I=\ngithub.com/Kwynto/gosession v0.2.3/go.mod h1:t9ebCczcC/w08whDLwShIZ59finO9EyRkiQf5B31m90=\ngithub.com/Kwynto/gosession v0.2.4 h1:SdK/0o1o88El3ALz/kFfZlrCfPBC6b6Czha6S5tb6vc=\ngithub.com/Kwynto/gosession v0.2.4/go.mod h1:t9ebCczcC/w08whDLwShIZ59finO9EyRkiQf5B31m90=\n"
  },
  {
    "path": "pkg/examples/example2/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/Kwynto/gosession\"\n)\n\nfunc homeHandler(w http.ResponseWriter, r *http.Request) {\n\tvar html string\n\theader := `\n\t<html>\n\t\t<head>\n\t\t\t<title>Title</title>\n\t\t</head>\n\t\t<body>\n\t\t\t<p>\n\t\t\t<a href=\"/\">Home Page</a><br>\n\t\t\t<a href=\"/firstpage\">First Page</a><br>\n\t\t\t<a href=\"/secondpage\">Second Page</a><br>\n\t\t\t<a href=\"/thirdpage\">Third Page</a><br>\n\t\t\t<a href=\"/fourthpage\">Fourth Page</a><br>\n\t\t\t<a href=\"/fifthpage\">Fifth Page</a><br>\n\t\t\t</p>\n\t\t\t<p>\n\t\t\tWebsite browsing history:<br>\n\t`\n\n\tfooter := `\n\t\t\t</p>\n\t\t</body>\n\t</html>\n\t`\n\n\tid := gosession.Start(&w, r)\n\ttransitions := id.Get(\"transitions\")\n\n\tif transitions == nil {\n\t\ttransitions = \"\"\n\t}\n\ttransitions = fmt.Sprint(transitions, \" \", r.RequestURI)\n\tid.Set(\"transitions\", transitions)\n\n\tmsg := fmt.Sprintf(\"%v\", transitions)\n\tmsg = strings.ReplaceAll(msg, \" \", \"<br>\")\n\thtml = fmt.Sprint(header, msg, footer)\n\n\tfmt.Fprint(w, html)\n}\n\nfunc favHandler(w http.ResponseWriter, r *http.Request) {\n\t// dummy\n}\n\nfunc main() {\n\tport := \":8080\"\n\n\thttp.HandleFunc(\"/favicon.ico\", favHandler)\n\thttp.HandleFunc(\"/\", homeHandler)\n\n\thttp.ListenAndServe(port, nil)\n}\n"
  },
  {
    "path": "pkg/examples/example3/cmd/web/handlers.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/Kwynto/gosession\"\n)\n\nfunc (app *application) home(w http.ResponseWriter, r *http.Request) {\n\tif r.URL.Path != \"/\" {\n\t\tapp.notFound(w)\n\t\treturn\n\t}\n\n\ttD := &templateData{User: \"\", Hash: \"\", Cart: []string{\"\"}, Transitions: []string{\"\"}}\n\n\tid := gosession.StartSecure(&w, r)\n\n\ttransitions := id.Get(\"transitions\")\n\tif transitions == nil {\n\t\ttransitions = \"\"\n\t}\n\ttransitions = fmt.Sprint(transitions, \" \", r.RequestURI)\n\tid.Set(\"transitions\", transitions)\n\ttStr := fmt.Sprintf(\"%v\", transitions)\n\ttStrs := strings.Split(tStr, \" \")\n\ttD.Transitions = tStrs\n\n\tcart := id.Get(\"cart\")\n\tif cart == nil {\n\t\tcart = \"\"\n\t\tid.Set(\"cart\", fmt.Sprint(cart))\n\t\ttD.Cart = []string{\"There's nothing here yet.\"}\n\t} else {\n\t\tsCart := fmt.Sprint(cart)\n\t\tprods := strings.Split(sCart, \" \")\n\t\ttD.Cart = prods\n\t}\n\n\tusername := id.Get(\"username\")\n\tif username != nil {\n\t\ttD.User = fmt.Sprint(username)\n\t\tapp.render(w, r, \"homeauth.page.tmpl\", tD)\n\t} else {\n\t\tapp.render(w, r, \"home.page.tmpl\", tD)\n\t}\n}\n\nfunc (app *application) authPage(w http.ResponseWriter, r *http.Request) {\n\tif r.Method != http.MethodPost {\n\t\tw.Header().Set(\"Allow\", http.MethodPost)\n\t\tapp.clientError(w, http.StatusMethodNotAllowed)\n\t\treturn\n\t}\n\n\tusername := r.FormValue(\"login\")\n\tpassword := r.FormValue(\"password\")\n\n\tid := gosession.StartSecure(&w, r)\n\n\tif username != \"\" && password != \"\" {\n\t\tpasHash := app.getMd5(password)\n\t\tid.Set(\"username\", username)\n\t\tid.Set(\"hash\", pasHash)\n\t}\n\n\ttransitions := id.Get(\"transitions\")\n\tif transitions == nil {\n\t\ttransitions = \"\"\n\t}\n\ttransitions = fmt.Sprint(transitions, \" \", r.RequestURI)\n\tid.Set(\"transitions\", transitions)\n\n\thttp.Redirect(w, r, \"/\", http.StatusSeeOther)\n}\n\nfunc (app *application) outPage(w http.ResponseWriter, r *http.Request) {\n\tid := gosession.StartSecure(&w, r)\n\tid.Remove(\"username\")\n\n\ttransitions := id.Get(\"transitions\")\n\tif transitions == nil {\n\t\ttransitions = \"\"\n\t}\n\ttransitions = fmt.Sprint(transitions, \" \", r.RequestURI)\n\tid.Set(\"transitions\", transitions)\n\n\thttp.Redirect(w, r, \"/\", http.StatusSeeOther)\n}\n\nfunc (app *application) buyPage(w http.ResponseWriter, r *http.Request) {\n\ttD := &templateData{User: \"\", Hash: \"\", Cart: []string{\"\"}, Transitions: []string{\"\"}}\n\n\tid := gosession.StartSecure(&w, r)\n\n\ttransitions := id.Get(\"transitions\")\n\tif transitions == nil {\n\t\ttransitions = \"\"\n\t}\n\ttransitions = fmt.Sprint(transitions, \" \", r.RequestURI)\n\tid.Set(\"transitions\", transitions)\n\ttStr := fmt.Sprintf(\"%v\", transitions)\n\ttStrs := strings.Split(tStr, \" \")\n\ttD.Transitions = tStrs\n\n\tcart := id.Get(\"cart\")\n\tif cart == nil {\n\t\tcart = \"\"\n\t}\n\tsCart := app.addProduct(fmt.Sprint(cart), app.convertProduct(r.RequestURI))\n\tid.Set(\"cart\", sCart)\n\tprods := strings.Split(sCart, \" \")\n\ttD.Cart = prods\n\n\tusername := id.Get(\"username\")\n\tif username != nil {\n\t\ttD.User = fmt.Sprint(username)\n\t\tapp.render(w, r, \"homeauth.page.tmpl\", tD)\n\t} else {\n\t\tapp.render(w, r, \"home.page.tmpl\", tD)\n\t}\n}\n"
  },
  {
    "path": "pkg/examples/example3/cmd/web/helpers.go",
    "content": "package main\n\nimport (\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"runtime/debug\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc (app *application) getMd5(text string) string {\n\th := md5.New()\n\th.Write([]byte(text))\n\treturn hex.EncodeToString(h.Sum(nil))\n}\n\nfunc (app *application) convertProduct(uri string) string {\n\tres := strings.Trim(uri, \"/\")\n\tres = strings.ToUpper(res)\n\treturn res\n}\n\nfunc (app *application) addProduct(text string, plus string) string {\n\tvar newText string = \"\"\n\tvar newProd string = \"\"\n\tvar isIt bool = false\n\n\tif text == \"\" {\n\t\tnewText = fmt.Sprintf(\"%s=%d \", plus, 1)\n\t\treturn newText\n\t}\n\n\tsplitTest := strings.Split(text, \" \")\n\tfor _, val := range splitTest {\n\t\tif val != \"\" {\n\t\t\tsplitVal := strings.Split(val, \"=\")\n\t\t\tprodName := splitVal[0]\n\t\t\tprodCount, _ := strconv.Atoi(splitVal[1])\n\t\t\tif prodName == plus {\n\t\t\t\tisIt = true\n\t\t\t\tprodCount += 1\n\t\t\t\tnewProd = fmt.Sprintf(\"%s=%d\", prodName, prodCount)\n\t\t\t\tnewText = fmt.Sprintf(\"%s%s \", newText, newProd)\n\t\t\t} else {\n\t\t\t\tnewText = fmt.Sprintf(\"%s%s \", newText, val)\n\t\t\t}\n\t\t}\n\t}\n\n\tif !isIt {\n\t\tnewProd = fmt.Sprintf(\"%s=%d\", plus, 1)\n\t\tnewText = fmt.Sprintf(\"%s%s \", newText, newProd)\n\t}\n\n\treturn newText\n}\n\nfunc (app *application) serverError(w http.ResponseWriter, err error) {\n\ttrace := fmt.Sprintf(\"%s\\n%s\", err.Error(), debug.Stack())\n\tapp.errorLog.Output(2, trace)\n\thttp.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)\n}\n\nfunc (app *application) clientError(w http.ResponseWriter, status int) {\n\thttp.Error(w, http.StatusText(status), status)\n}\n\nfunc (app *application) notFound(w http.ResponseWriter) {\n\tapp.clientError(w, http.StatusNotFound)\n}\n\nfunc (app *application) render(w http.ResponseWriter, r *http.Request, name string, td *templateData) {\n\t// We extract the corresponding set of templates from the cache, depending on the page name\n\tts, ok := app.templateCache[name]\n\tif !ok {\n\t\tapp.serverError(w, fmt.Errorf(\"template %s not exist\", name))\n\t\treturn\n\t}\n\n\t// Render template files by passing dynamic data from the `td` variable.\n\terr := ts.Execute(w, td)\n\tif err != nil {\n\t\tapp.serverError(w, err)\n\t}\n}\n"
  },
  {
    "path": "pkg/examples/example3/cmd/web/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"html/template\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\n// Creating an `application` structure to store the dependencies of the entire web application.\ntype application struct {\n\terrorLog      *log.Logger\n\tinfoLog       *log.Logger\n\ttemplateCache map[string]*template.Template\n}\n\n// Structure for configuration\ntype Config struct {\n\tAddr      string\n\tStaticDir string\n}\n\nfunc main() {\n\t// Reading flags from the application launch bar.\n\tcfg := new(Config)\n\tflag.StringVar(&cfg.Addr, \"addr\", \":8080\", \"HTTP network address\")\n\tflag.StringVar(&cfg.StaticDir, \"static-dir\", \"./ui/static\", \"Path to static assets\")\n\tflag.Parse()\n\n\t// Creating loggers\n\tinfoLog := log.New(os.Stdout, \"INFO\\t\", log.Ldate|log.Ltime)\n\terrorLog := log.New(os.Stderr, \"ERROR\\t\", log.Ldate|log.Ltime|log.Lshortfile)\n\n\t// Initialize the template cache.\n\ttemplateCache, err := newTemplateCache(\"./ui/html/\")\n\tif err != nil {\n\t\terrorLog.Fatal(err)\n\t}\n\n\t// Initialize the structure with the application dependencies.\n\tapp := &application{\n\t\terrorLog:      errorLog,\n\t\tinfoLog:       infoLog,\n\t\ttemplateCache: templateCache,\n\t}\n\n\t// Server structure with address, logger and routing\n\tsrv := &http.Server{\n\t\tAddr:     cfg.Addr,\n\t\tErrorLog: errorLog,\n\t\tHandler:  app.routes(),\n\t}\n\n\t// Starting the server\n\tapp.infoLog.Printf(\"Start server: %s\", cfg.Addr)\n\terr = srv.ListenAndServe()\n\tapp.errorLog.Fatal(err)\n}\n"
  },
  {
    "path": "pkg/examples/example3/cmd/web/routes.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"path/filepath\"\n)\n\ntype neuteredFileSystem struct {\n\tfs http.FileSystem\n}\n\n// Blocking direct access to the file system\nfunc (nfs neuteredFileSystem) Open(path string) (http.File, error) {\n\tf, err := nfs.fs.Open(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ts, _ := f.Stat()\n\tif s.IsDir() {\n\t\tindex := filepath.Join(path, \"index.html\")\n\t\tif _, err := nfs.fs.Open(index); err != nil {\n\t\t\tcloseErr := f.Close()\n\t\t\tif closeErr != nil {\n\t\t\t\treturn nil, closeErr\n\t\t\t}\n\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn f, nil\n}\n\nfunc (app *application) routes() *http.ServeMux {\n\t// Routes\n\tmux := http.NewServeMux()\n\tmux.HandleFunc(\"/\", app.home)\n\tmux.HandleFunc(\"/auth\", app.authPage)\n\tmux.HandleFunc(\"/logout\", app.outPage)\n\tmux.HandleFunc(\"/product1\", app.buyPage)\n\tmux.HandleFunc(\"/product2\", app.buyPage)\n\tmux.HandleFunc(\"/product3\", app.buyPage)\n\n\tfileServer := http.FileServer(neuteredFileSystem{http.Dir(\"./ui/static\")})\n\tmux.Handle(\"/static\", http.NotFoundHandler())\n\tmux.Handle(\"/static/\", http.StripPrefix(\"/static\", fileServer))\n\n\treturn mux\n}\n"
  },
  {
    "path": "pkg/examples/example3/cmd/web/templates.go",
    "content": "package main\n\nimport (\n\t\"html/template\"\n\t\"path/filepath\"\n)\n\n// Structure for the data template\ntype templateData struct {\n\tUser        string\n\tHash        string\n\tCart        []string\n\tTransitions []string\n}\n\nfunc newTemplateCache(dir string) (map[string]*template.Template, error) {\n\tcache := map[string]*template.Template{}\n\n\t// We use the filepath.Glob function to get a slice of all file paths with the extension '.page.tmpl'.\n\tpages, err := filepath.Glob(filepath.Join(dir, \"*.page.tmpl\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// We iterate through the template file from each page.\n\tfor _, page := range pages {\n\t\t// Extracting the final file name\n\t\tname := filepath.Base(page)\n\n\t\t// Processing the iterated template file.\n\t\tts, err := template.ParseFiles(page)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// We use the ParseGlob method to add all the wireframe templates.\n\t\tts, err = ts.ParseGlob(filepath.Join(dir, \"*.layout.tmpl\"))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// We use the ParseGlob method to add all auxiliary templates.\n\t\tts, err = ts.ParseGlob(filepath.Join(dir, \"*.partial.tmpl\"))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// Adding the resulting set of templates to the cache using the page name\n\t\tcache[name] = ts\n\t}\n\n\t// We return the received map.\n\treturn cache, nil\n}\n"
  },
  {
    "path": "pkg/examples/example3/go.mod",
    "content": "module example3\n\ngo 1.17\n\nrequire github.com/Kwynto/gosession v0.2.4\n"
  },
  {
    "path": "pkg/examples/example3/go.sum",
    "content": "github.com/Kwynto/gosession v0.2.1 h1:d0Dz1IBcjQ44nkAWkOz7hYEYrCfkyIjOIMnBhVy/GiE=\ngithub.com/Kwynto/gosession v0.2.1/go.mod h1:t9ebCczcC/w08whDLwShIZ59finO9EyRkiQf5B31m90=\ngithub.com/Kwynto/gosession v0.2.3 h1:8Ag0nXX0cCxrbFOFdCdPI9u46YtEEG/JFx1iHF+PH6I=\ngithub.com/Kwynto/gosession v0.2.3/go.mod h1:t9ebCczcC/w08whDLwShIZ59finO9EyRkiQf5B31m90=\ngithub.com/Kwynto/gosession v0.2.4 h1:SdK/0o1o88El3ALz/kFfZlrCfPBC6b6Czha6S5tb6vc=\ngithub.com/Kwynto/gosession v0.2.4/go.mod h1:t9ebCczcC/w08whDLwShIZ59finO9EyRkiQf5B31m90=\n"
  },
  {
    "path": "pkg/examples/example3/ui/html/base.layout.tmpl",
    "content": "{{define \"base\"}}\n<!doctype html>\n<html lang='en'>\n<head>\n    <meta charset='utf-8'>\n    <title>{{template \"title\" .}}</title>\n    <link rel='stylesheet' href='/static/css/main.css'>\n    <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,700'>\n</head>\n<body>\n    <header>\n        <h1><a href='/'>Home Page</a></h1>\n    </header>\n    <nav>\n        <a href='/'>Home Page</a>\n    </nav>\n    <main>\n        {{template \"main\" .}}\n    </main>\n    {{template \"footer\" .}}\n    <script src=\"/static/js/main.js\" type=\"text/javascript\"></script>\n</body>\n</html>\n{{end}}"
  },
  {
    "path": "pkg/examples/example3/ui/html/footer.partial.tmpl",
    "content": "{{define \"footer\"}}\n<footer>This is the third example of a <strong>GoLang</strong> site using <strong>GoSession</strong>.</footer>\n{{end}}"
  },
  {
    "path": "pkg/examples/example3/ui/html/home.page.tmpl",
    "content": "{{template \"base\" .}}\n\n{{define \"title\"}}Home Page{{end}}\n\n{{define \"main\"}}\n\t<table>\n\t\t<tr>\n\t\t\t<td rowspan=\"2\" valign=\"top\">\n\t\t\t\t<p>\n\t\t\t\t\t<h2>Authorization</h2>\n\t\t\t\t\t<form action=\"/auth\" method=\"post\" class=\"form-horizontal\">\n\t\t\t\t\t\t<input name=\"login\" type=\"text\" value=\"\" placeholder=\"Login\" required pattern=\"^[a-zA-Z0-9_-]+$\">\n\t\t\t\t\t\t<input name=\"password\" type=\"password\" value=\"\" placeholder=\"Password\" required pattern=\"^[a-zA-Z0-9]+$\">\n\t\t\t\t\t\t<button name=\"signin\" type=\"submit\">Auth button</button>\n\t\t\t\t\t</form>\n\t\t\t\t\t<br><br>\n\t\t\t\t</p>\n\t\t\t\t<p>\n\t\t\t\t\t<h2>Links:</h2>\n\t\t\t\t\t<a href=\"/product1\">Buy Product No. 1</a><br><br>\n\t\t\t\t\t<a href=\"/product2\">Buy Product No. 2</a><br><br>\n\t\t\t\t\t<a href=\"/product3\">Buy Product No. 3</a><br><br>\n\t\t\t\t</p>\n\t\t\t</td>\n\t\t\t<td width=\"250px\">\n\t\t\t\t<p>\n\t\t\t\t\t<h2>Shopping cart</h2>\n\t\t\t\t\t{{ range $key, $pr := .Cart }}\n\t\t\t\t\t\t{{ $pr }}<br>\n\t\t\t\t\t{{end}}\n\t\t\t\t</p>\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t<p>\n\t\t\t\t\t<h2>Browsing history:</h2>\n\t\t\t\t\t{{ range $key, $tr := .Transitions }}\n\t\t\t\t\t\t{{ $tr }}<br>\n\t\t\t\t\t{{end}}\n\t\t\t\t</p>\n\t\t\t</td>\n\t\t</tr>\n\t</table>\n{{end}}"
  },
  {
    "path": "pkg/examples/example3/ui/html/homeauth.page.tmpl",
    "content": "{{template \"base\" .}}\n\n{{define \"title\"}}Home Page{{end}}\n\n{{define \"main\"}}\n\t<table>\n\t\t<tr>\n\t\t\t<td rowspan=\"2\" valign=\"top\">\n\t\t\t\t<p>\n\t\t\t\t\t<h3>You are logged in as: </h3>{{.User}} <a href=\"/logout\">Log Out</a><br><br>\n\t\t\t\t</p>\n\t\t\t\t<p>\n\t\t\t\t\t<h2>Links:</h2>\n\t\t\t\t\t<a href=\"/product1\">Buy Product No. 1</a><br><br>\n\t\t\t\t\t<a href=\"/product2\">Buy Product No. 2</a><br><br>\n\t\t\t\t\t<a href=\"/product3\">Buy Product No. 3</a><br><br>\n\t\t\t\t</p>\n\t\t\t</td>\n\t\t\t<td width=\"250px\">\n\t\t\t\t<p>\n\t\t\t\t\t<h2>Shopping cart</h2>\n\t\t\t\t\t{{ range $key, $pr := .Cart }}\n\t\t\t\t\t\t{{ $pr }}<br>\n\t\t\t\t\t{{end}}\n\t\t\t\t</p>\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t<p>\n\t\t\t\t\t<h2>Browsing history:</h2>\n\t\t\t\t\t{{ range $key, $tr := .Transitions }}\n\t\t\t\t\t\t{{ $tr }}<br>\n\t\t\t\t\t{{end}}\n\t\t\t\t</p>\n\t\t\t</td>\n\t\t</tr>\n\t</table>\n{{end}}"
  },
  {
    "path": "pkg/examples/example3/ui/static/css/main.css",
    "content": "* {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n    font-size: 18px;\n    font-family: \"Ubuntu Mono\", monospace;\n}\n\nhtml, body {\n    height: 100%;\n}\n\nbody {\n    line-height: 1.5;\n    background-color: #F1F3F6;\n    color: #34495E;\n    overflow-y: scroll;\n}\n\nheader, nav, main, footer {\n    padding: 2px calc((100% - 800px) / 2) 0;\n}\n\nmain {\n    margin-top: 54px;\n    margin-bottom: 54px;\n    min-height: calc(100vh - 345px);\n    overflow: auto;\n}\n\nh1 a {\n    font-size: 36px;\n    font-weight: bold;\n    background-image: url(\"/static/img/logo.png\");\n    background-repeat: no-repeat;\n    background-position: 0px 0px;\n    height: 36px;\n    padding-left: 50px;\n    position: relative;\n}\n\nh1 a:hover {\n    text-decoration: none;\n    color: #34495E;\n}\n\nh2 {\n    font-size: 22px;\n    margin-bottom: 36px;\n    position: relative;\n    top: -9px;\n}\n\na {\n    color: #62CB31;\n    text-decoration: none;\n}\n\na:hover {\n    color: #4EB722;\n    text-decoration: underline;\n}\n\ntextarea, input:not([type=\"submit\"]) {\n    font-size: 18px;\n    font-family: \"Ubuntu Mono\", monospace;\n}\n\nheader {\n    background-image: -webkit-linear-gradient(left, #34495e, #34495e 25%, #9b59b6 25%, #9b59b6 35%, #3498db 35%, #3498db 45%, #62cb31 45%, #62cb31 55%, #ffb606 55%, #ffb606 65%, #e67e22 65%, #e67e22 75%, #e74c3c 85%, #e74c3c 85%, #c0392b 85%, #c0392b 100%);\n    background-image: -moz-linear-gradient(left, #34495e, #34495e 25%, #9b59b6 25%, #9b59b6 35%, #3498db 35%, #3498db 45%, #62cb31 45%, #62cb31 55%, #ffb606 55%, #ffb606 65%, #e67e22 65%, #e67e22 75%, #e74c3c 85%, #e74c3c 85%, #c0392b 85%, #c0392b 100%);\n    background-image: -ms-linear-gradient(left, #34495e, #34495e 25%, #9b59b6 25%, #9b59b6 35%, #3498db 35%, #3498db 45%, #62cb31 45%, #62cb31 55%, #ffb606 55%, #ffb606 65%, #e67e22 65%, #e67e22 75%, #e74c3c 85%, #e74c3c 85%, #c0392b 85%, #c0392b 100%);\n    background-image: linear-gradient(to right, #34495e, #34495e 25%, #9b59b6 25%, #9b59b6 35%, #3498db 35%, #3498db 45%, #62cb31 45%, #62cb31 55%, #ffb606 55%, #ffb606 65%, #e67e22 65%, #e67e22 75%, #e74c3c 85%, #e74c3c 85%, #c0392b 85%, #c0392b 100%);\n    background-size: 100% 6px;\n    background-repeat: no-repeat;\n    border-bottom: 1px solid #E4E5E7;\n    overflow: auto;\n    padding-top: 33px;\n    padding-bottom: 27px;\n    text-align: center;\n}\n\nheader a {\n    color: #34495E;\n    text-decoration: none;\n}\n\nnav {\n    border-bottom: 1px solid #E4E5E7;\n    padding-top: 17px;\n    padding-bottom: 15px;\n    background: #F7F9FA;\n    height: 60px;\n    color: #6A6C6F;\n}\n\nnav a {\n    margin-right: 1.5em;\n    display: inline-block;\n}\n\nnav form {\n    display: inline-block;\n    margin-left: 1.5em;\n}\n\nnav div {\n    width: 50%;\n    float: left;\n}\n\nnav div:last-child {\n    text-align: right;\n}\n\nnav div:last-child a {\n    margin-left: 1.5em;\n    margin-right: 0;\n}\n\nnav a.live {\n    color: #34495E;\n    cursor: default;\n}\n\nnav a.live:hover {\n    text-decoration: none;\n}\n\nnav a.live:after {\n    content: '';\n    display: block;\n    position: relative;\n    left: calc(50% - 7px);\n    top: 9px;\n    width: 14px;\n    height: 14px;\n    background: #F7F9FA;\n    border-left: 1px solid #E4E5E7;\n    border-bottom: 1px solid #E4E5E7;\n    -moz-transform: rotate(45deg);\n    -webkit-transform: rotate(-45deg);\n}\n\nform div {\n    margin-bottom: 18px;\n}\n\nform div:last-child {\n    border-top: 1px dashed #E4E5E7;\n}\n\nform input[type=\"radio\"] {\n    position: relative;\n    top: 2px;\n    margin-left: 18px;\n}\n\nform input[type=\"text\"], form input[type=\"password\"], form input[type=\"email\"] {\n    padding: 0.75em 18px;\n    width: 100%;\n}\n\nform input[type=text], form input[type=\"password\"], form input[type=\"email\"], textarea {\n    color: #6A6C6F;\n    background: #FFFFFF;\n    border: 1px solid #E4E5E7;\n    border-radius: 3px;\n}\n\nform label {\n    display: inline-block;\n    margin-bottom: 9px;\n}\n\n.error {\n    color: #C0392B;\n    font-weight: bold;\n    display: block;\n}\n\n.error + textarea, .error + input {\n    border-color: #C0392B !important;\n    border-width: 2px !important;\n}\n\ntextarea {\n    padding: 18px;\n    width: 100%;\n    height: 266px;\n}\n\nbutton {\n    background-color: #4CAF50;\n    border: none;\n    color: white;\n    padding: 15px 32px;\n    text-align: center;\n    text-decoration: none;\n    display: inline-block;\n    font-size: 16px;\n    margin: 4px 2px;\n    cursor: pointer;\n    width: 100%;\n}\n\nbutton:hover {\n    background-color: #5865f4;\n    color: white;\n}\n\n.snippet {\n    background-color: #FFFFFF;\n    border: 1px solid #E4E5E7;\n    border-radius: 3px;\n}\n\n.snippet pre {\n    padding: 18px;\n    border-top: 1px solid #E4E5E7;\n    border-bottom: 1px solid #E4E5E7;\n}\n\n.snippet .metadata {\n    background-color: #F7F9FA;\n    color: #6A6C6F;\n    padding: 0.75em 18px;\n    overflow: auto;\n}\n\n.snippet .metadata span {\n    float: right;\n}\n\n.snippet .metadata strong {\n    color: #34495E;\n}\n\n.snippet .metadata time {\n    display: inline-block;\n}\n\n.snippet .metadata time:first-child {\n    float: left;\n}\n\n.snippet .metadata time:last-child {\n    float: right;\n}\n\ndiv.flash {\n    color: #FFFFFF;\n    font-weight: bold;\n    background-color: #34495E;\n    padding: 18px;\n    margin-bottom: 36px;\n    text-align: center;\n}\n\ndiv.error {\n    color: #FFFFFF;\n    background-color: #C0392B;\n    padding: 18px;\n    margin-bottom: 36px;\n    font-weight: bold;\n    text-align: center;\n}\n\ntable {\n    background: white;\n    border: 1px solid #E4E5E7;\n    border-collapse: collapse;\n    width: 100%;\n}\n\ntd, th {\n    text-align: left;\n    padding: 9px 18px;\n}\n\nth:last-child, td:last-child {\n    text-align: right;\n    color: #6A6C6F;\n}\n\ntr {\n    border-bottom: 1px solid #E4E5E7;\n}\n\ntr:nth-child(2n) {\n    background-color: #F7F9FA;\n}\n\nfooter {\n    border-top: 1px solid #E4E5E7;\n    padding-top: 17px;\n    padding-bottom: 15px;\n    background: #F7F9FA;\n    height: 60px;\n    color: #6A6C6F;\n    text-align: center;\n}\n"
  },
  {
    "path": "pkg/examples/example3/ui/static/js/main.js",
    "content": "var navLinks = document.querySelectorAll(\"nav a\");\nfor (var i = 0; i < navLinks.length; i++) {\n\tvar link = navLinks[i]\n\tif (link.getAttribute('href') == window.location.pathname) {\n\t\tlink.classList.add(\"live\");\n\t\tbreak;\n\t}\n}"
  }
]