diff --git a/.fossa.yml b/.fossa.yml deleted file mode 100644 index cb3f48eeb6..0000000000 --- a/.fossa.yml +++ /dev/null @@ -1,11 +0,0 @@ -version: 2 -cli: - server: https://app.fossa.com - fetcher: custom - project: https://github.com/kataras/iris.git -analyze: - modules: - - name: iris - type: go - target: . - path: . \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 680144f5e6..d0118e62de 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -2,7 +2,7 @@ Examples for the Iris project can be found at . Documentation for the Iris project can be found at -. +. Love iris? Please consider supporting the project: 👉 https://iris-go.com/donate diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b1ecdfba39..e26c40a533 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -30,7 +30,7 @@ Please make sure the bug is reproducible over the `master` branch: ```sh $ cd PROJECT -$ go get -u github.com/kataras/iris/v12@master +$ go get -u github.com/kataras/iris@master $ go run . ``` diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 579e611bb0..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -sudo: false -language: go - -os: - - linux - - osx -go: - - 1.14.x - - 1.15.x - - master -go_import_path: github.com/kataras/iris/v12 -env: - global: - - GO111MODULE=on -install: - - go get ./... -script: - - go test -count=1 -v -cover -race ./... -after_script: - # examples - - cd ./_examples - - go get ./... - - go test -count=1 -v -cover -race ./... - - cd ../ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index d2b9a657c0..0000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,74 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or -advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at kataras2006@hotmail.com. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index a2d19c614f..0000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,33 +0,0 @@ -# Contributing - -First of all read our [Code of Conduct](https://github.com/kataras/iris/blob/master/CODE_OF_CONDUCT.md). - -## PR - -1. Open a new [issue](https://github.com/kataras/iris/issues/new) - * Write version of your local Iris. - * Write version of your local Go programming language. - * Describe your problem, what did you expect to see and what you see instead. - * If it's a feature request, describe your idea as better as you can - * optionally, navigate to the [chat](https://chat.iris-go.com) to push other members to participate and share their thoughts about your brilliant idea. -2. Fork the [repository](https://github.com/kataras/iris). -3. Make your changes. -4. Compare & Push the PR from [here](https://github.com/kataras/iris/compare). - -## Translate - -We need your help with translations into your native language. - -Iris needs your help, please think about contributing to the translation of the [README](README.md) and https://iris-go.com, you will be rewarded. - -Instructions can be found at: https://github.com/kataras/iris/issues/796 - -## Share - -### Writing - -Write an article about Iris in https://medium.com , https://dev.to or if you're being a hackathon at https://hackernoon.com, some [examples](https://github.com/kataras/iris/wiki/Publications). - -### Social networks - -If you're part of any social network, do a post(or tweet if twitter) about Iris and what you love about it, many examples can be found, the most recent one is [that](https://twitter.com/DorMoshe/status/1154486477247508480). diff --git a/FAQ.md b/FAQ.md deleted file mode 100644 index 3703c32a5a..0000000000 --- a/FAQ.md +++ /dev/null @@ -1,56 +0,0 @@ -# FAQ - -## [![iris](https://img.shields.io/badge/iris-powered-2196f3.svg?style=for-the-badge)](https://github.com/kataras/iris) - -Add a `badge` to your open-source projects powered by [Iris](https://iris-go.com) by pasting the below code snippet to the project repo's README.md: - -```md -[![iris](https://img.shields.io/badge/iris-powered-2196f3.svg?style=for-the-badge)](https://github.com/kataras/iris) -``` - -## Editors & IDEs Extensions - -### Visual Studio Code - - - -> Please feel free to list your own Iris extension(s) here by [PR](https://github.com/kataras/iris/pulls) - -## How to upgrade - -```sh -go get -u github.com/kataras/iris/v12@master -``` - -Go version 1.13 and above is required. - -## Learning - -More than 180 practical examples, tutorials and articles at: - -- https://iris-go.com/start -- https://bit.ly/iris-req-book -- https://github.com/kataras/iris/wiki/Starter-kits -- https://github.com/kataras/iris/tree/master/_examples -- https://godoc.org/github.com/kataras/iris - -## Active development mode - -Iris may have reached version 12, but we're not stopping there. We have many feature ideas on our board that we're anxious to add and other innovative web development solutions that we're planning to build into Iris. - -## Can I find a job if I learn how to use Iris? - -Yes, not only because you will learn Golang in the same time, but there are some positions -open for Iris-specific developers the time we speak. - -Go to our facebook page, like it and receive notifications about new job offers, we already have couple of them stay at the top of the page: https://www.facebook.com/iris.framework - -## Do we have a Community chat? - -Yes, https://chat.iris-go.com - -## How is the development of Iris economically supported? - -By people like you, who help us by donating small or large amounts of money. - -Help this project deliver awesome and unique features with the highest possible code quality by donating any amount via [PayPal](https://iris-go.com/donate). Your name will be published [here](https://iris-go.com) after your approval via e-mail. diff --git a/HISTORY.md b/HISTORY.md deleted file mode 100644 index d4deb1ac9b..0000000000 --- a/HISTORY.md +++ /dev/null @@ -1,829 +0,0 @@ - - -# Changelog - -### Looking for free and real-time support? - - https://github.com/kataras/iris/issues - https://chat.iris-go.com - -### Looking for previous versions? - - https://github.com/kataras/iris/releases - -### Want to be hired? - - https://facebook.com/iris.framework - -### Should I upgrade my Iris? - -Developers are not forced to upgrade if they don't really need it. Upgrade whenever you feel ready. - -**How to upgrade**: Open your command-line and execute this command: `go get github.com/kataras/iris/v12@latest`. - -# Next - -This release introduces new features and some breaking changes inside the `mvc` and `hero` packages. -The codebase for dependency injection has been simplified a lot (fewer LOCs and easier to read and follow up). - -The new release contains a fresh new and awesome feature....**a function dependency can accept previous registered dependencies and update or return a new value of any type**. - -The new implementation is **faster** on both design and serve-time. - -The most common scenario from a route to handle is to: -- accept one or more path parameters and request data, a payload -- send back a response, a payload (JSON, XML,...) - -The new Iris Dependency Injection feature is about **33.2% faster** than its predecessor on the above case. This drops down even more the performance cost between native handlers and dynamic handlers with dependencies. This reason itself brings us, with safety and performance-wise, to the new `Party.ConfigureContainer(builder ...func(*iris.APIContainer)) *APIContainer` method which returns methods such as `Handle(method, relativePath string, handlersFn ...interface{}) *Route` and `RegisterDependency`. - -Look how clean your codebase can be when using Iris': - -```go -package main - -import "github.com/kataras/iris/v12" - -type ( - testInput struct { - Email string `json:"email"` - } - - testOutput struct { - ID int `json:"id"` - Name string `json:"name"` - } -) - -func handler(id int, in testInput) testOutput { - return testOutput{ - ID: id, - Name: in.Email, - } -} - -func main() { - app := iris.New() - app.ConfigureContainer(func(api *iris.APIContainer) { - api.Post("/{id:int}", handler) - }) - app.Listen(":5000", iris.WithOptimizations) -} -``` - -Your eyes don't lie you. You read well, no `ctx.ReadJSON(&v)` and `ctx.JSON(send)` neither `error` handling are presented. It is a huge relief but if you ever need, you still have the control over those, even errors from dependencies. Here is a quick list of the new Party.ConfigureContainer()'s fields and methods: - -```go -// Container holds the DI Container of this Party featured Dependency Injection. -// Use it to manually convert functions or structs(controllers) to a Handler. -Container *hero.Container -``` - -```go -// OnError adds an error handler for this Party's DI Hero Container and its handlers (or controllers). -// The "errorHandler" handles any error may occurred and returned -// during dependencies injection of the Party's hero handlers or from the handlers themselves. -OnError(errorHandler func(iris.Context, error)) -``` - -```go -// RegisterDependency adds a dependency. -// The value can be a single struct value or a function. -// Follow the rules: -// * {structValue} -// * func(accepts ) returns or (, error) -// * func(accepts iris.Context) returns or (, error) -// -// A Dependency can accept a previous registered dependency and return a new one or the same updated. -// * func(accepts1 , accepts2 ) returns or (, error) or error -// * func(acceptsPathParameter1 string, id uint64) returns or (, error) -// -// Usage: -// -// - RegisterDependency(loggerService{prefix: "dev"}) -// - RegisterDependency(func(ctx iris.Context) User {...}) -// - RegisterDependency(func(User) OtherResponse {...}) -RegisterDependency(dependency interface{}) - -// UseResultHandler adds a result handler to the Container. -// A result handler can be used to inject the returned struct value -// from a request handler or to replace the default renderer. -UseResultHandler(handler func(next iris.ResultHandler) iris.ResultHandler) -``` - -
ResultHandler - -```go -type ResultHandler func(ctx iris.Context, v interface{}) error -``` -
- -```go -// Use same as a common Party's "Use" but it accepts dynamic functions as its "handlersFn" input. -Use(handlersFn ...interface{}) -// Done same as a common Party's but it accepts dynamic functions as its "handlersFn" input. -Done(handlersFn ...interface{}) -``` - -```go -// Handle same as a common Party's `Handle` but it accepts one or more "handlersFn" functions which each one of them -// can accept any input arguments that match with the Party's registered Container's `Dependencies` and -// any output result; like custom structs , string, []byte, int, error, -// a combination of the above, hero.Result(hero.View | hero.Response) and more. -// -// It's common from a hero handler to not even need to accept a `Context`, for that reason, -// the "handlersFn" will call `ctx.Next()` automatically when not called manually. -// To stop the execution and not continue to the next "handlersFn" -// the end-developer should output an error and return `iris.ErrStopExecution`. -Handle(method, relativePath string, handlersFn ...interface{}) *Route - -// Get registers a GET route, same as `Handle("GET", relativePath, handlersFn....)`. -Get(relativePath string, handlersFn ...interface{}) *Route -// and so on... -``` - -Prior to this version the `iris.Context` was the only one dependency that has been automatically binded to the handler's input or a controller's fields and methods, read below to see what types are automatically binded: - -| Type | Maps To | -|------|:---------| -| [*mvc.Application](https://pkg.go.dev/github.com/kataras/iris/v12/mvc?tab=doc#Application) | Current MVC Application | -| [iris.Context](https://pkg.go.dev/github.com/kataras/iris/v12/context?tab=doc#Context) | Current Iris Context | -| [*sessions.Session](https://pkg.go.dev/github.com/kataras/iris/v12/sessions?tab=doc#Session) | Current Iris Session | -| [context.Context](https://golang.org/pkg/context/#Context) | [ctx.Request().Context()](https://golang.org/pkg/net/http/#Request.Context) | -| [*http.Request](https://golang.org/pkg/net/http/#Request) | `ctx.Request()` | -| [http.ResponseWriter](https://golang.org/pkg/net/http/#ResponseWriter) | `ctx.ResponseWriter()` | -| [http.Header](https://golang.org/pkg/net/http/#Header) | `ctx.Request().Header` | -| [time.Time](https://golang.org/pkg/time/#Time) | `time.Now()` | -| [*golog.Logger](https://pkg.go.dev/github.com/kataras/golog) | Iris Logger | -| [net.IP](https://golang.org/pkg/net/#IP) | `net.ParseIP(ctx.RemoteAddr())` | -| `string`, | | -| `int, int8, int16, int32, int64`, | | -| `uint, uint8, uint16, uint32, uint64`, | | -| `float, float32, float64`, | | -| `bool`, | | -| `slice` | [Path Parameter](https://github.com/kataras/iris/wiki/Routing-path-parameter-types) | -| Struct | [Request Body](https://github.com/kataras/iris/tree/master/_examples/request-body) of `JSON`, `XML`, `YAML`, `Form`, `URL Query`, `Protobuf`, `MsgPack` | - -Here is a preview of what the new Hero handlers look like: - -### Request & Response & Path Parameters - -**1.** Declare Go types for client's request body and a server's response. - -```go -type ( - request struct { - Firstname string `json:"firstname"` - Lastname string `json:"lastname"` - } - - response struct { - ID uint64 `json:"id"` - Message string `json:"message"` - } -) -``` - -**2.** Create the route handler. - -Path parameters and request body are binded automatically. -- **id uint64** binds to "id:uint64" -- **input request** binds to client request data such as JSON - -```go -func updateUser(id uint64, input request) response { - return response{ - ID: id, - Message: "User updated successfully", - } -} -``` - -**3.** Configure the container per group and register the route. - -```go -app.Party("/user").ConfigureContainer(container) - -func container(api *iris.APIContainer) { - api.Put("/{id:uint64}", updateUser) -} -``` - -**4.** Simulate a [client](https://curl.haxx.se/download.html) request which sends data to the server and displays the response. - -```sh -curl --request PUT -d '{"firstanme":"John","lastname":"Doe"}' http://localhost:8080/user/42 -``` - -```json -{ - "id": 42, - "message": "User updated successfully" -} -``` - -### Custom Preflight - -Before we continue to the next section, register dependencies, you may want to learn how a response can be customized through the `iris.Context` right before sent to the client. - -The server will automatically execute the `Preflight(iris.Context) error` method of a function's output struct value right before send the response to the client. - -Take for example that you want to fire different HTTP status codes depending on the custom logic inside your handler and also modify the value(response body) itself before sent to the client. Your response type should contain a `Preflight` method like below. - -```go -type response struct { - ID uint64 `json:"id,omitempty"` - Message string `json:"message"` - Code int `json:"code"` - Timestamp int64 `json:"timestamp,omitempty"` -} - -func (r *response) Preflight(ctx iris.Context) error { - if r.ID > 0 { - r.Timestamp = time.Now().Unix() - } - - ctx.StatusCode(r.Code) - return nil -} -``` - -Now, each handler that returns a `*response` value will call the `response.Preflight` method automatically. - -```go -func deleteUser(db *sql.DB, id uint64) *response { - // [...custom logic] - - return &response{ - Message: "User has been marked for deletion", - Code: iris.StatusAccepted, - } -} -``` - -If you register the route and fire a request you should see an output like this, the timestamp is filled and the HTTP status code of the response that the client will receive is 202 (Status Accepted). - -```json -{ - "message": "User has been marked for deletion", - "code": 202, - "timestamp": 1583313026 -} -``` - -### Register Dependencies - -**1.** Import packages to interact with a database. -The go-sqlite3 package is a database driver for [SQLite](https://www.sqlite.org/index.html). - -```go -import "database/sql" -import _ "github.com/mattn/go-sqlite3" -``` - -**2.** Configure the container ([see above](#request--response--path-parameters)), register your dependencies. Handler expects an *sql.DB instance. - -```go -localDB, _ := sql.Open("sqlite3", "./foo.db") -api.RegisterDependency(localDB) -``` - -**3.** Register a route to create a user. - -```go -api.Post("/{id:uint64}", createUser) -``` - -**4.** The create user Handler. - -The handler accepts a database and some client request data such as JSON, Protobuf, Form, URL Query and e.t.c. It Returns a response. - -```go -func createUser(db *sql.DB, user request) *response { - // [custom logic using the db] - userID, err := db.CreateUser(user) - if err != nil { - return &response{ - Message: err.Error(), - Code: iris.StatusInternalServerError, - } - } - - return &response{ - ID: userID, - Message: "User created", - Code: iris.StatusCreated, - } -} -``` - -**5.** Simulate a [client](https://curl.haxx.se/download.html) to create a user. - -```sh -# JSON -curl --request POST -d '{"firstname":"John","lastname":"Doe"}' \ ---header 'Content-Type: application/json' \ -http://localhost:8080/user -``` - -```sh -# Form (multipart) -curl --request POST 'http://localhost:8080/users' \ ---header 'Content-Type: multipart/form-data' \ ---form 'firstname=John' \ ---form 'lastname=Doe' -``` - -```sh -# Form (URL-encoded) -curl --request POST 'http://localhost:8080/users' \ ---header 'Content-Type: application/x-www-form-urlencoded' \ ---data-urlencode 'firstname=John' \ ---data-urlencode 'lastname=Doe' -``` - -```sh -# URL Query -curl --request POST 'http://localhost:8080/users?firstname=John&lastname=Doe' -``` - -Response: - -```json -{ - "id": 42, - "message": "User created", - "code": 201, - "timestamp": 1583313026 -} -``` - -Other Improvements: - -- Fix `AutoTLS` when used with `iris.TLSNoRedirect` [*](https://github.com/kataras/iris/issues/1577). The `AutoTLS` runner can be customized through the new `iris.AutoTLSNoRedirect` instead, read its go documentation. Example of having both TLS and non-TLS versions of the same application without conflicts with letsencrypt `./well-known` path: - -![](https://iris-go.com/images/github/autotls-1.png) - -```go -package main - -import ( - "net/http" - "time" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - app.Logger().SetLevel("debug") - - app.Get("/", func(ctx iris.Context) { - ctx.JSON(iris.Map{ - "time": time.Now().Unix(), - "tls": ctx.Request().TLS != nil, - }) - }) - - var fallbackServer = func(acme func(http.Handler) http.Handler) *http.Server { - srv := &http.Server{Handler: acme(app)} - go srv.ListenAndServe() - return srv - } - - app.Run(iris.AutoTLS(":443", "example.com", "mail@example.com", - iris.AutoTLSNoRedirect(fallbackServer))) -} -``` - -- `Application.UseRouter(...Handler)` - per party to register handlers before the main router, useful on handlers that should control whether the router itself should ran or not. Independently of the incoming request's method and path values. These handlers will be executed ALWAYS against ALL incoming matched requests. Example of use-case: CORS. - -- `*versioning.Group` type is a full `Party` now. - -- `Party.UseOnce` - either inserts a middleware, or on the basis of the middleware already existing, replace that existing middleware instead. - -- Ability to register a view engine per group of routes or for the current chain of handlers through `Party.RegisterView` and `Context.ViewEngine` respectfully. - -- Add [Blocks](_examples/view/template_blocks_0) template engine. - -- Add [Ace](_examples/view/template_ace_0) template parser to the view engine and other minor improvements. - -- Fix huge repo size of 55.7MB, which slows down the overall Iris installation experience. Now, go-get performs ~3 times faster. I 've managed it using the [bfg-repo-cleaner](https://github.com/rtyley/bfg-repo-cleaner) tool - an alternative to git-filter-branch command. Watch the small gif below to learn how: - -[![](https://media.giphy.com/media/U8560aiWTurW4iAOLn/giphy.gif)](https://media.giphy.com/media/U8560aiWTurW4iAOLn/giphy.gif) - -- [gRPC](https://grpc.io/) features: - - New Router [Wrapper](middleware/grpc). - - New MVC `.Handle(ctrl, mvc.GRPC{...})` option which allows to register gRPC services per-party (without the requirement of a full wrapper) and optionally strict access to gRPC clients only, see the [example here](_examples/mvc/grpc-compatible). - -- Improved tracing (with `app.Logger().SetLevel("debug")`) for routes. Example: - -#### DBUG Routes (1) - -![DBUG routes](https://iris-go.com/images/v12.2.0-dbug.png?v=0) - -#### DBUG Routes (2) - -![DBUG routes](https://iris-go.com/images/v12.2.0-dbug2.png?v=0) - -- Add `Configuration.RemoteAddrHeadersForce bool` to force `Context.RemoteAddr() string` to return the first entry of request headers as a fallback instead of the `Request.RemoteAddr` one, as requested at: [1567#issuecomment-663972620](https://github.com/kataras/iris/issues/1567#issuecomment-663972620). - -- Fix [#1569#issuecomment-663739177](https://github.com/kataras/iris/issues/1569#issuecomment-663739177). - -- Fix [#1564](https://github.com/kataras/iris/issues/1564). - -- Fix [#1553](https://github.com/kataras/iris/issues/1553). - -- New `DirOptions.Cache` to cache assets in-memory among with their compressed contents (in order to be ready to served if client ask). Learn more about this feature by reading [all #1556 comments](https://github.com/kataras/iris/issues/1556#issuecomment-661057446). Usage: - -```go -var dirOpts = DirOptions{ - // [...other options] - Cache: DirCacheOptions{ - Enable: true, - // Don't compress files smaller than 300 bytes. - CompressMinSize: 300, - // Ignore compress already compressed file types - // (some images and pdf). - CompressIgnore: iris.MatchImagesAssets, - // Gzip, deflate, br(brotli), snappy. - Encodings: []string{"gzip", "deflate", "br", "snappy"}, - // Log to the stdout the total reduced file size. - Verbose: 1, - }, -} -``` - -- New `DirOptions.PushTargets` and `PushTargetsRegexp` to push index' assets to the client without additional requests. Inspirated by issue [#1562](https://github.com/kataras/iris/issues/1562). Example matching all `.js, .css and .ico` files (recursively): - -```go -var dirOpts = iris.DirOptions{ - // [...other options] - IndexName: "/index.html", - PushTargetsRegexp: map[string]*regexp.Regexp{ - "/": regexp.MustCompile("((.*).js|(.*).css|(.*).ico)$"), - // OR: - // "/": iris.MatchCommonAssets, - }, - Compress: true, -} -``` - -- Update jet parser to v4.0.2, closes [#1551](https://github.com/kataras/iris/issues/1551). It contains two breaking changes by its author: - - Relative paths on `extends, import, include...` tmpl functions, e.g. `{{extends "../layouts/application.jet"}}` instead of `layouts/application.jet` - - the new [jet.Ranger](https://github.com/CloudyKit/jet/pull/165) interface now requires a `ProvidesIndex() bool` method too - - Example has been [updated](https://github.com/kataras/iris/tree/master/_examples/view/template_jet_0) - -- Fix [#1552](https://github.com/kataras/iris/issues/1552). - -- Proper listing of root directories on `Party.HandleDir` when its `DirOptions.ShowList` was set to true. - - Customize the file/directory listing page through views, see [example](https://github.com/kataras/iris/tree/master/_examples/file-server/file-server). - -- Socket Sharding as requested at [#1544](https://github.com/kataras/iris/issues/1544). New `iris.WithSocketSharding` Configurator and `SocketSharding bool` setting. - -- Versioned Controllers feature through the new `mvc.Version` option. See [_examples/mvc/versioned-controller](https://github.com/kataras/iris/blob/master/_examples/mvc/versioned-controller/main.go). - -- Fix [#1539](https://github.com/kataras/iris/issues/1539). - -- New [rollbar example](https://github.com/kataras/iris/blob/master/_examples/logging/rollbar/main.go). - -- New builtin [requestid](https://github.com/kataras/iris/tree/master/middleware/requestid) middleware. - -- New builtin [JWT](https://github.com/kataras/iris/tree/master/middleware/jwt) middleware based on [square/go-jose](https://github.com/square/go-jose) featured with optional encryption to set claims with sensitive data when necessary. - -- New `iris.RouteOverlap` route registration rule. `Party.SetRegisterRule(iris.RouteOverlap)` to allow overlapping across multiple routes for the same request subdomain, method, path. See [1536#issuecomment-643719922](https://github.com/kataras/iris/issues/1536#issuecomment-643719922). This allows two or more **MVC Controllers** to listen on the same path based on one or more registered dependencies (see [_examples/mvc/authenticated-controller](https://github.com/kataras/iris/tree/master/_examples/mvc/authenticated-controller)). - -- `Context.ReadForm` now can return an `iris.ErrEmptyForm` instead of `nil` when the new `Configuration.FireEmptyFormError` is true (when `iris.WithEmptyFormError` is set) on missing form body to read from. - -- `Configuration.EnablePathIntelligence | iris.WithPathIntelligence` to enable path intelligence automatic path redirection on the most closest path (if any), [example]((https://github.com/kataras/iris/blob/master/_examples/routing/intelligence/main.go) - -- Enhanced cookie security and management through new `Context.AddCookieOptions` method and new cookie options (look on New Package-level functions section below), [securecookie](https://github.com/kataras/iris/tree/master/_examples/cookies/securecookie) example has been updated. -- `Context.RemoveCookie` removes also the Request's specific cookie of the same request lifecycle when `iris.CookieAllowReclaim` is set to cookie options, [example](https://github.com/kataras/iris/tree/master/_examples/cookies/options). - -- `iris.TLS` can now accept certificates in form of raw `[]byte` contents too. -- `iris.TLS` registers a secondary http server which redirects "http://" to their "https://" equivalent requests, unless the new `iris.TLSNoRedirect` host Configurator is provided on `iris.TLS`, e.g. `app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key", iris.TLSNoRedirect))`. There is `iris.AutoTLSNoRedirect` option for `AutoTLS` too. - -- Fix an [issue](https://github.com/kataras/i18n/issues/1) about i18n loading from path which contains potential language code. - -- Server will not return neither log the `ErrServerClosed` error if `app.Shutdown` was called manually via interrupt signal(CTRL/CMD+C), note that if the server closed by any other reason the error will be fired as previously (unless `iris.WithoutServerError(iris.ErrServerClosed)`). - -- Finally, Log level's and Route debug information colorization is respected across outputs. Previously if the application used more than one output destination (e.g. a file through `app.Logger().AddOutput`) the color support was automatically disabled from all, including the terminal one, this problem is fixed now. Developers can now see colors in their terminals while log files are kept with clear text. - -- New `iris.WithLowercaseRouting` option which forces all routes' paths to be lowercase and converts request paths to their lowercase for matching. - -- New `app.Validator { Struct(interface{}) error }` field and `app.Validate` method were added. The `app.Validator = ` can be used to integrate a 3rd-party package such as [go-playground/validator](https://github.com/go-playground/validator). If set-ed then Iris `Context`'s `ReadJSON`, `ReadXML`, `ReadMsgPack`, `ReadYAML`, `ReadForm`, `ReadQuery`, `ReadBody` methods will return the validation error on data validation failures. The [read-json-struct-validation](_examples/request-body/read-json-struct-validation) example was updated. - -- A result of can implement the new `hero.PreflightResult` interface which contains a single method of `Preflight(iris.Context) error`. If this method exists on a custom struct value which is returned from a handler then it will fire that `Preflight` first and if not errored then it will cotninue by sending the struct value as JSON(by-default) response body. - -- `ctx.JSON, JSONP, XML`: if `iris.WithOptimizations` is NOT passed on `app.Run/Listen` then the indentation defaults to `" "` (four spaces) and `" "` respectfully otherwise it is empty or the provided value. - -- Hero Handlers (and `app.ConfigureContainer().Handle`) do not have to require `iris.Context` just to call `ctx.Next()` anymore, this is done automatically now. - -- Improve Remote Address parsing as requested at: [#1453](https://github.com/kataras/iris/issues/1453). Add `Configuration.RemoteAddrPrivateSubnets` to exclude those addresses when fetched by `Configuration.RemoteAddrHeaders` through `context.RemoteAddr() string`. - -- Fix [#1487](https://github.com/kataras/iris/issues/1487). - -- Fix [#1473](https://github.com/kataras/iris/issues/1473). - -New Package-level Variables: - -- `iris.DirListRichOptions` to pass on `iris.DirListRich` method. -- `iris.DirListRich` to override the default look and feel if the `DirOptions.ShowList` was set to true, can be passed to `DirOptions.DirList` field. -- `DirOptions.PushTargets` for http/2 push on index [*](https://github.com/kataras/iris/tree/master/_examples/file-server/http2push/main.go). -- `iris.Compression` middleware to compress responses and decode compressed request data respectfully. -- `iris.B, KB, MB, GB, TB, PB, EB` for byte units. -- `TLSNoRedirect` to disable automatic "http://" to "https://" redirections (see below) -- `CookieAllowReclaim`, `CookieAllowSubdomains`, `CookieSameSite`, `CookieSecure` and `CookieEncoding` to bring previously sessions-only features to all cookies in the request. - -New Context Methods: - -- `Context.ViewEngine(ViewEngine)` to set a view engine on-fly for the current chain of handlers, responsible to render templates through `ctx.View`. [Example](_examples/view/context-view-engine). -- `Context.SetErr(error)` and `Context.GetErr() error` helpers. -- `Context.CompressWriter(bool) error` and `Context.CompressReader(bool) error`. -- `Context.Clone() Context` returns a copy of the Context. -- `Context.IsCanceled() bool` reports whether the request has been canceled by the client. -- `Context.IsSSL() bool` reports whether the request is under HTTPS SSL (New `Configuration.SSLProxyHeaders` and `HostProxyHeaders` fields too). -- `Context.CompressReader(enable bool)` method and `iris.CompressReader` middleware to enable future request read body calls to decompress data, [example](_examples/compression/main.go). -- `Context.RegisterDependency(v interface{})` and `Context.UnregisterDependency(typ reflect.Type)` to register/remove struct dependencies on serve-time through a middleware. -- `Context.SetID(id interface{})` and `Context.GetID() interface{}` added to register a custom unique indetifier to the Context, if necessary. -- `Context.GetDomain() string` returns the domain. -- `Context.AddCookieOptions(...CookieOption)` adds options for `SetCookie`, `SetCookieKV, UpsertCookie` and `RemoveCookie` methods for the current request. -- `Context.ClearCookieOptions()` clears any cookie options registered through `AddCookieOptions`. -- `Context.SetLanguage(langCode string)` force-sets a language code from inside a middleare, similar to the `app.I18n.ExtractFunc` -- `Context.ServeContentWithRate`, `ServeFileWithRate` and `SendFileWithRate` methods to throttle the "download" speed of the client -- `Context.IsHTTP2() bool` reports whether the protocol version for incoming request was HTTP/2 -- `Context.IsGRPC() bool` reports whether the request came from a gRPC client -- `Context.UpsertCookie(*http.Cookie, cookieOptions ...context.CookieOption)` upserts a cookie, fixes [#1485](https://github.com/kataras/iris/issues/1485) too -- `Context.StopWithStatus(int)` stops the handlers chain and writes the status code -- `Context.StopWithText(int, string)` stops the handlers chain, writes thre status code and a plain text message -- `Context.StopWithError(int, error)` stops the handlers chain, writes thre status code and the error's message -- `Context.StopWithJSON(int, interface{})` stops the handlers chain, writes the status code and sends a JSON response -- `Context.StopWithProblem(int, iris.Problem)` stops the handlers, writes the status code and sends an `application/problem+json` response -- `Context.Protobuf(proto.Message)` sends protobuf to the client (note that the `Context.JSON` is able to send protobuf as JSON) -- `Context.MsgPack(interface{})` sends msgpack format data to the client -- `Context.ReadProtobuf(ptr)` binds request body to a proto message -- `Context.ReadJSONProtobuf(ptr, ...options)` binds JSON request body to a proto message -- `Context.ReadMsgPack(ptr)` binds request body of a msgpack format to a struct -- `Context.ReadBody(ptr)` binds the request body to the "ptr" depending on the request's Method and Content-Type -- `Context.ReflectValue() []reflect.Value` stores and returns the `[]reflect.ValueOf(ctx)` -- `Context.Controller() reflect.Value` returns the current MVC Controller value. - -Breaking Changes: - -- `versioning.NewGroup(string)` now accepts a `Party` as its first input argument: `NewGroup(Party, string)`. -- `versioning.RegisterGroups` is **removed** as it is no longer necessary. -- `Configuration.RemoteAddrHeaders` from `map[string]bool` to `[]string`. If you used `With(out)RemoteAddrHeader` then you are ready to proceed without any code changes for that one. -- `ctx.Gzip(boolean)` replaced with `ctx.CompressWriter(boolean) error`. -- `ctx.GzipReader(boolean) error` replaced with `ctx.CompressReader(boolean) error`. -- `iris.Gzip` and `iris.GzipReader` replaced with `iris.Compression` (middleware). -- `ctx.ClientSupportsGzip() bool` replaced with `ctx.ClientSupportsEncoding("gzip", "br" ...) bool`. -- `ctx.GzipResponseWriter()` is **removed**. -- `Party.HandleDir/iris.FileServer` now accepts a `http.FileSystem` instead of a string and returns a list of `[]*Route` (GET and HEAD) instead of GET only. Write: `app.HandleDir("/", iris.Dir("./assets"))` instead of `app.HandleDir("/", "./assets")` and `DirOptions.Asset, AssetNames, AssetInfo` removed, use `go-bindata -fs [..]` and `app.HandleDir("/", AssetFile())` instead. -- `Context.OnClose` and `Context.OnCloseConnection` now both accept an `iris.Handler` instead of a simple `func()` as their callback. -- `Context.StreamWriter(writer func(w io.Writer) bool)` changed to `StreamWriter(writer func(w io.Writer) error) error` and it's now the `Context.Request().Context().Done()` channel that is used to receive any close connection/manual cancel signals, instead of the deprecated `ResponseWriter().CloseNotify()` one. Same for the `Context.OnClose` and `Context.OnCloseConnection` methods. -- Fixed handler's error response not be respected when response recorder was used instead of the common writer. Fixes [#1531](https://github.com/kataras/iris/issues/1531). It contains a **BREAKING CHANGE** of: the new `Configuration.ResetOnFireErrorCode` field should be set **to true** in order to behave as it used before this update (to reset the contents on recorder). -- `Context.String()` (rarely used by end-developers) it does not return a unique string anymore, to achieve the old representation you must call the new `Context.SetID` method first. -- `iris.CookieEncode` and `CookieDecode` are replaced with the `iris.CookieEncoding`. -- `sessions#Config.Encode` and `Decode` are removed in favor of (the existing) `Encoding` field. -- `versioning.GetVersion` now returns an empty string if version wasn't found. -- Change the MIME type of `Javascript .js` and `JSONP` as the HTML specification now recommends to `"text/javascript"` instead of the obselete `"application/javascript"`. This change was pushed to the `Go` language itself as well. See . -- Remove the last input argument of `enableGzipCompression` in `Context.ServeContent`, `ServeFile` methods. This was deprecated a few versions ago. A middleware (`app.Use(iris.CompressWriter)`) or a prior call to `Context.CompressWriter(true)` will enable compression. Also these two methods and `Context.SendFile` one now support `Content-Range` and `Accept-Ranges` correctly out of the box (`net/http` had a bug, which is now fixed). -- `Context.ServeContent` no longer returns an error, see `ServeContentWithRate`, `ServeFileWithRate` and `SendFileWithRate` new methods too. -- `route.Trace() string` changed to `route.Trace(w io.Writer)`, to achieve the same result just pass a `bytes.Buffer` -- `var mvc.AutoBinding` removed as the default behavior now resolves such dependencies automatically (see [[FEATURE REQUEST] MVC serving gRPC-compatible controller](https://github.com/kataras/iris/issues/1449)). -- `mvc#Application.SortByNumMethods()` removed as the default behavior now binds the "thinnest" empty `interface{}` automatically (see [MVC: service injecting fails](https://github.com/kataras/iris/issues/1343)). -- `mvc#BeforeActivation.Dependencies().Add` should be replaced with `mvc#BeforeActivation.Dependencies().Register` instead -- **REMOVE** the `kataras/iris/v12/typescript` package in favor of the new [iris-cli](https://github.com/kataras/iris-cli). Also, the alm typescript online editor was removed as it is deprecated by its author, please consider using the [designtsx](https://designtsx.com/) instead. - -There is a breaking change on the type alias of `iris.Context` which now points to the `*context.Context` instead of the `context.Context` interface. The **interface has been removed** and the ability to **override** the Context **is not** available any more. When we added the ability from end-developers to override the Context years ago, we have never imagine that we will ever had such a featured Context with more than 4000 lines of code. As of Iris 2020, it is difficult and un-productive from an end-developer to override the Iris Context, and as far as we know, nobody uses this feature anymore because of that exact reason. Beside the overriding feature support end, if you still use the `context.Context` instead of `iris.Context`, it's the time to do it: please find-and-replace to `iris.Context` as wikis, book and all examples shows for the past 3 years. For the 99.9% of the users there is no a single breaking change, you already using `iris.Context` so you are in the "safe zone". - -# Su, 16 February 2020 | v12.1.8 - -New Features: - -- [[FEATURE REQUEST] MVC serving gRPC-compatible controller](https://github.com/kataras/iris/issues/1449) - -Fixes: - -- [App can't find embedded pug template files by go-bindata](https://github.com/kataras/iris/issues/1450) - -New Examples: - -- [_examples/mvc/grpc-compatible](_examples/mvc/grpc-compatible) - -# Mo, 10 February 2020 | v12.1.7 - -Implement **new** `SetRegisterRule(iris.RouteOverride, RouteSkip, RouteError)` to resolve: https://github.com/kataras/iris/issues/1448 - -New Examples: - -- [_examples/routing/route-register-rule](_examples/routing/route-register-rule) - -# We, 05 February 2020 | v12.1.6 - -Fixes: - -- [jet.View - urlpath error](https://github.com/kataras/iris/issues/1438) -- [Context.ServeFile send 'application/wasm' with a wrong extra field](https://github.com/kataras/iris/issues/1440) - -# Su, 02 February 2020 | v12.1.5 - -Various improvements and linting. - -# Su, 29 December 2019 | v12.1.4 - -Minor fix on serving [embedded files](https://github.com/kataras/iris/wiki/File-server). - -# We, 25 December 2019 | v12.1.3 - -Fix [[BUG] [iris.Default] RegisterView](https://github.com/kataras/iris/issues/1410) - -# Th, 19 December 2019 | v12.1.2 - -Fix [[BUG]Session works incorrectly when meets the multi-level TLDs](https://github.com/kataras/iris/issues/1407). - -# Mo, 16 December 2019 | v12.1.1 - -Add [Context.FindClosest(n int) []string](https://github.com/kataras/iris/blob/master/_examples/routing/intelligence/manual/main.go#L22) - -```go -app := iris.New() -app.OnErrorCode(iris.StatusNotFound, notFound) -``` - -```go -func notFound(ctx iris.Context) { - suggestPaths := ctx.FindClosest(3) - if len(suggestPaths) == 0 { - ctx.WriteString("404 not found") - return - } - - ctx.HTML("Did you mean?
    ") - for _, s := range suggestPaths { - ctx.HTML(`
  • %s
  • `, s, s) - } - ctx.HTML("
") -} -``` - -![](https://iris-go.com/images/iris-not-found-suggests.png) - -# Fr, 13 December 2019 | v12.1.0 - -## Breaking Changes - -Minor as many of you don't even use them but, indeed, they need to be covered here. - -- Old i18n middleware(iris/middleware/i18n) was replaced by the [i18n](i18n) sub-package which lives as field at your application: `app.I18n.Load(globPathPattern string, languages ...string)` (see below) -- Community-driven i18n middleware(iris-contrib/middleware/go-i18n) has a `NewLoader` function which returns a loader which can be passed at `app.I18n.Reset(loader i18n.Loader, languages ...string)` to change the locales parser -- The Configuration's `TranslateFunctionContextKey` was replaced by `LocaleContextKey` which Context store's value (if i18n is used) returns the current Locale which contains the translate function, the language code, the language tag and the index position of it -- The `context.Translate` method was replaced by `context.Tr` as a shortcut for the new `context.GetLocale().GetMessage(format, args...)` method and it matches the view's function `{{tr format args}}` too -- If you used [Iris Django](https://github.com/kataras/iris/tree/master/_examples/view/template_django_0) view engine with `import _ github.com/flosch/pongo2-addons` you **must change** the import path to `_ github.com/iris-contrib/pongo2-addons` or add a [go mod replace](https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive) to your `go.mod` file, e.g. `replace github.com/flosch/pongo2-addons => github.com/iris-contrib/pongo2-addons v0.0.1`. - -## Fixes - -All known issues. - -1. [#1395](https://github.com/kataras/iris/issues/1395) -2. [#1369](https://github.com/kataras/iris/issues/1369) -3. [#1399](https://github.com/kataras/iris/issues/1399) with PR [#1400](https://github.com/kataras/iris/pull/1400) -4. [#1401](https://github.com/kataras/iris/issues/1401) -5. [#1406](https://github.com/kataras/iris/issues/1406) -6. [neffos/#20](https://github.com/kataras/neffos/issues/20) -7. [pio/#5](https://github.com/kataras/pio/issues/5) - -## New Features - -### Internationalization and localization - -Support for i18n is now a **builtin feature** and is being respected across your entire application, per say [sitemap](https://github.com/kataras/iris/wiki/Sitemap) and [views](https://github.com/kataras/iris/blob/master/_examples/i18n/main.go#L50). - -Refer to the wiki section: https://github.com/kataras/iris/wiki/Sitemap for details. - -### Sitemaps - -Iris generates and serves one or more [sitemap.xml](https://www.sitemaps.org/protocol.html) for your static routes. - -Navigate through: https://github.com/kataras/iris/wiki/Sitemap for more. - -## New Examples - -2. [_examples/i18n](_examples/i18n) -1. [_examples/sitemap](_examples/routing/sitemap) -3. [_examples/desktop/blink](_examples/desktop/blink) -4. [_examples/desktop/lorca](_examples/desktop/lorca) -5. [_examples/desktop/webview](_examples/desktop/webview) - -# Sa, 26 October 2019 | v12.0.0 - -- Add version suffix of the **import path**, learn why and see what people voted at [issue #1370](https://github.com/kataras/iris/issues/1370) - -![](https://iris-go.com/images/vote-v12-version-suffix_26_oct_2019.png) - -- All errors are now compatible with go1.13 `errors.Is`, `errors.As` and `fmt.Errorf` and a new `core/errgroup` package created -- Fix [#1383](https://github.com/kataras/iris/issues/1383) -- Report whether system couldn't find the directory of view templates -- Remove the `Party#GetReport` method, keep `Party#GetReporter` which is an `error` and an `errgroup.Group`. -- Remove the router's deprecated methods such as StaticWeb and StaticEmbedded_XXX -- The `Context#CheckIfModifiedSince` now returns an `context.ErrPreconditionFailed` type of error when client conditions are not met. Usage: `if errors.Is(err, context.ErrPreconditionFailed) { ... }` -- Add `SourceFileName` and `SourceLineNumber` to the `Route`, reports the exact position of its registration inside your project's source code. -- Fix a bug about the MVC package route binding, see [PR #1364](https://github.com/kataras/iris/pull/1364) -- Add `mvc/Application#SortByNumMethods` as requested at [#1343](https://github.com/kataras/iris/issues/1343#issuecomment-524868164) -- Add status code `103 Early Hints` -- Fix performance of session.UpdateExpiration on 200 thousands+ keys with new radix as reported at [issue #1328](https://github.com/kataras/iris/issues/1328) -- New redis session database configuration field: `Driver: redis.Redigo()` or `redis.Radix()`, see [updated examples](_examples/sessions/database/redis/) -- Add Clusters support for redis:radix session database (`Driver: redis:Radix()`) as requested at [issue #1339](https://github.com/kataras/iris/issues/1339) -- Create Iranian [README_FA](README_FA.md) translation with [PR #1360](https://github.com/kataras/iris/pull/1360) -- Create Korean [README_KO](README_KO.md) translation with [PR #1356](https://github.com/kataras/iris/pull/1356) -- Create Spanish [README_ES](README_ES.md) and [HISTORY_ES](HISTORY_ES.md) translations with [PR #1344](https://github.com/kataras/iris/pull/1344). - -The iris-contrib/middleare and examples are updated to use the new `github.com/kataras/iris/v12` import path. - -# Fr, 16 August 2019 | v11.2.8 - -- Set `Cookie.SameSite` to `Lax` when subdomains sessions share is enabled[*](https://github.com/kataras/iris/commit/6bbdd3db9139f9038641ce6f00f7b4bab6e62550) -- Add and update all [experimental handlers](https://github.com/iris-contrib/middleware) -- New `XMLMap` function which wraps a `map[string]interface{}` and converts it to a valid xml content to render through `Context.XML` method -- Add new `ProblemOptions.XML` and `RenderXML` fields to render the `Problem` as XML(application/problem+xml) instead of JSON("application/problem+json) and enrich the `Negotiate` to easily accept the `application/problem+xml` mime. - -Commit log: https://github.com/kataras/iris/compare/v11.2.7...v11.2.8 - -# Th, 15 August 2019 | v11.2.7 - -This minor version contains improvements on the Problem Details for HTTP APIs implemented on [v11.2.5](#mo-12-august-2019--v1125). - -- Fix https://github.com/kataras/iris/issues/1335#issuecomment-521319721 -- Add `ProblemOptions` with `RetryAfter` as requested at: https://github.com/kataras/iris/issues/1335#issuecomment-521330994. -- Add `iris.JSON` alias for `context#JSON` options type. - -[Example](https://github.com/kataras/iris/blob/45d7c6fedb5adaef22b9730592255f7bb375e809/_examples/routing/http-errors/main.go#L85) and [wikis](https://github.com/kataras/iris/wiki/Routing-error-handlers#the-problem-type) updated. - -References: - -- https://tools.ietf.org/html/rfc7231#section-7.1.3 -- https://tools.ietf.org/html/rfc7807 - -Commit log: https://github.com/kataras/iris/compare/v11.2.6...v11.2.7 - -# We, 14 August 2019 | v11.2.6 - -Allow [handle more than one route with the same paths and parameter types but different macro validation functions](https://github.com/kataras/iris/issues/1058#issuecomment-521110639). - -```go -app.Get("/{alias:string regexp(^[a-z0-9]{1,10}\\.xml$)}", PanoXML) -app.Get("/{alias:string regexp(^[a-z0-9]{1,10}$)}", Tour) -``` - -Commit log: https://github.com/kataras/iris/compare/v11.2.5...v11.2.6 - -# Mo, 12 August 2019 | v11.2.5 - -- [New Feature: Problem Details for HTTP APIs](https://github.com/kataras/iris/pull/1336) -- [Add Context.AbsoluteURI](https://github.com/kataras/iris/pull/1336/files#diff-15cce7299aae8810bcab9b0bf9a2fdb1R2368) - -Commit log: https://github.com/kataras/iris/compare/v11.2.4...v11.2.5 - -# Fr, 09 August 2019 | v11.2.4 - -- Fixes [iris.Jet: no view engine found for '.jet' or '.html'](https://github.com/kataras/iris/issues/1327) -- Fixes [ctx.ViewData not work with JetEngine](https://github.com/kataras/iris/issues/1330) -- **New Feature**: [HTTP Method Override](https://github.com/kataras/iris/issues/1325) -- Fixes [Poor performance of session.UpdateExpiration on 200 thousands+ keys with new radix lib](https://github.com/kataras/iris/issues/1328) by introducing the `sessions.Config.Driver` configuration field which defaults to `Redigo()` but can be set to `Radix()` too, future additions are welcomed. - -Commit log: https://github.com/kataras/iris/compare/v11.2.3...v11.2.4 - -# Tu, 30 July 2019 | v11.2.3 - -- [New Feature: Handle different parameter types in the same path](https://github.com/kataras/iris/issues/1315) -- [New Feature: Content Negotiation](https://github.com/kataras/iris/issues/1319) -- [Context.ReadYAML](https://github.com/kataras/iris/tree/master/_examples/request-body/read-yaml) -- Fixes https://github.com/kataras/neffos/issues/1#issuecomment-515698536 - -# We, 24 July 2019 | v11.2.2 - -Sessions as middleware: - -```go -import "github.com/kataras/iris/v12/sessions" -// [...] - -app := iris.New() -sess := sessions.New(sessions.Config{...}) - -app.Get("/path", func(ctx iris.Context){ - session := sessions.Get(ctx) - // [work with session...] -}) -``` - -- Add `Session.Len() int` to return the total number of stored values/entries. -- Make `Context.HTML` and `Context.Text` to accept an optional, variadic, `args ...interface{}` input arg(s) too. - -## v11.1.1 - -- https://github.com/kataras/iris/issues/1298 -- https://github.com/kataras/iris/issues/1207 - -# Tu, 23 July 2019 | v11.2.0 - -Read about the new release at: https://www.facebook.com/iris.framework/posts/3276606095684693 diff --git a/HISTORY_ES.md b/HISTORY_ES.md deleted file mode 100644 index 20cc339801..0000000000 --- a/HISTORY_ES.md +++ /dev/null @@ -1,141 +0,0 @@ - - -# Registro de cambios - -### ¿Buscando soporte gratuito y en tiempo real? - - https://github.com/kataras/iris/issues - https://chat.iris-go.com - -### ¿Buscando versiones anteriores? - - https://github.com/kataras/iris/releases - -### ¿Quieres ser contratado? - - https://facebook.com/iris.framework - -### ¿Debo actualizar mi versión de Iris? - -Los desarrolladores no están obligados a actualizar si realmente no lo necesitan. Actualice siempre que se sienta listo. - -**Cómo actualizar**: Abra su línea de comandos y ejecute este comando: `go get github.com/kataras/iris/v12@latest`. - -# Su, 16 February 2020 | v12.1.8 - -Not translated yet, please navigate to the [english version](HISTORY.md#su-16-february-2020--v1218) instead. - -# Sábado, 26 de octubre 2019 | v12.0.0 - -- Add version suffix of the **import path**, learn why and see what people voted at [issue #1370](https://github.com/kataras/iris/issues/1370) - -![](https://iris-go.com/images/vote-v12-version-suffix_26_oct_2019.png) - - -- Todos los errores ahora son compatibles con `errors.Is`, `errors.As` y `fmt.Errorf` de go1.13 y ha sido creado un nuevo paquete `core/errgroup` -- Corrección [#1383](https://github.com/kataras/iris/issues/1383) -- Informar en cualquier sistema si no se logró encontrar directorio de plantillas para las vistas. -- Se removió el método `Party#GetReport`, se mantuvo `Party#GetReporter` que es un `error` y `errgroup.Group`. -- Se removieron métodos obsoletos del enrutador como StaticWeb y StaticEmbedded_XXX -- `Context#CheckIfModifiedSince` ahora returna tipo error `context.ErrPreconditionFailed` cuando no se cumplen condiciones del cliente. Uso: `if errors.Is(err, context.ErrPreconditionFailed) { ... }` -- Se agregó `SourceFileName` y `SourceLineNumber` a `Route`, informan la posición exacta de su registro dentro del código fuente de su proyecto. -- Se corrige bug sobre enlace de ruta del paquete MVC, ver [PR #1364](https://github.com/kataras/iris/pull/1364) -- Se agregó `mvc/Application#SortByNumMethods` solicitado en [#1343](https://github.com/kataras/iris/issues/1343#issuecomment-524868164) -- Código de estado `103 Early Hints` agregado. -- Se corrigió rendimiento de `session.UpdateExpiration` en nas de 200 mil registros con nuevo radix reportado en [problema #1328](https://github.com/kataras/iris/issues/1328) -- Nuevo campo de configuración de la base de datos de sesión de redis: `Driver: redis.Redigo()` o `redis.Radix()`, ver [ejemplos actualizados](_examples/sessions/database/redis/) -- Se agregó soporte de Clusters para la base de datos de sesión redis: radix (`Driver: redis: Radix ()`) como se solicitó en [problema #1339](https://github.com/kataras/iris/issues/1339) -- Se creó traducción en iraní [README_FA](README_FA.md) en [PR #1360](https://github.com/kataras/iris/pull/1360) -- Se creó traducción en koreano [README_KO](README_KO.md) en [PR #1356](https://github.com/kataras/iris/pull/1356) -- Se creó traducción en español [README_ES](README_ES.md) y [HISTORY_ES](HISTORY_ES.md) en [PR #1344](https://github.com/kataras/iris/pull/1344). - -iris-contrib/middleare y ejemplos se actualizaron para utilizar la nueva ruta de importación `github.com/kataras/iris/v12`. - -# Viernes, 16 de agosto 2019 | v11.2.8 - -- Establecer `Cookie.SameSite` como `Lax` cuando el uso compartido de sesiones de subdominios esté habilitado[*](https://github.com/kataras/iris/commit/6bbdd3db9139f9038641ce6f00f7b4bab6e62550) -- Agregados y actualizados todos los [Handlers experimentales](https://github.com/kataras/iris/tree/master/_examples/experimental-handlers) -- Nueva función `XMLMap` que envuelve un `map[string]interface{}` y la convierte en un contenido xml válido para representarlo a través del método `Context.XML` -- Se agregaron nuevos campos `ProblemOptions.XML` y ` RenderXML` para renderizar `Problem` como XML(application/problem+xml) en lugar de JSON("application/problem+json) y enriquezca el `Negotiate` para aceptar fácilmente el mime type `application/problem+xml`. - -Registro de commits: https://github.com/kataras/iris/compare/v11.2.7...v11.2.8 - -# Jueves, 15 de agosto 2019 | v11.2.7 - -Esta versión menor contiene mejoras en los Detalles del problema para las API HTTP implementadas en [v11.2.5](#lunes-12-de-agosto-2019--v1125). - -- Ajuste https://github.com/kataras/iris/issues/1335#issuecomment-521319721 -- Agregado `ProblemOptions` con `RetryAfter` como se solicitó en: https://github.com/kataras/iris/issues/1335#issuecomment-521330994. -- Agregado alias `iris.JSON` para el tipo de opciones `context#JSON`. - -[Ejemplos](https://github.com/kataras/iris/blob/45d7c6fedb5adaef22b9730592255f7bb375e809/_examples/routing/http-errors/main.go#L85) y [wikis](https://github.com/kataras/iris/wiki/Routing-error-handlers#the-problem-type) actualizados. - -Referencias: - -- https://tools.ietf.org/html/rfc7231#section-7.1.3 -- https://tools.ietf.org/html/rfc7807 - -Registro de commits: https://github.com/kataras/iris/compare/v11.2.6...v11.2.7 - -# Miércoles, 14 de agosto 2019 | v11.2.6 - -Permitir [manejar más de una ruta con las mismas rutas y tipos de parámetros pero diferentes funciones de validación de macros](https://github.com/kataras/iris/issues/1058#issuecomment-521110639). - -```go -app.Get("/{alias:string regexp(^[a-z0-9]{1,10}\\.xml$)}", PanoXML) -app.Get("/{alias:string regexp(^[a-z0-9]{1,10}$)}", Tour) -``` - -Registro de commits: https://github.com/kataras/iris/compare/v11.2.5...v11.2.6 - -# Lunes, 12 de agosto 2019 | v11.2.5 - -- [Nueva característica: Detalle del problemas para las APIs HTTP](https://github.com/kataras/iris/pull/1336) -- [Agregado Context.AbsoluteURI](https://github.com/kataras/iris/pull/1336/files#diff-15cce7299aae8810bcab9b0bf9a2fdb1R2368) - -Registro de commits: https://github.com/kataras/iris/compare/v11.2.4...v11.2.5 - -# Viernes, 09 de agosto 2019 | v11.2.4 - -- Ajustes [iris.Jet: no view engine found for '.jet' or '.html'](https://github.com/kataras/iris/issues/1327) -- Ajustes [ctx.ViewData no funciona con JetEngine](https://github.com/kataras/iris/issues/1330) -- **Nueva característica**: [Override de métodos HTTP](https://github.com/kataras/iris/issues/1325) -- Ajustes [Bajo rendimiento en session.UpdateExpiration en más de 200 mil keys con nueva librería radix](https://github.com/kataras/iris/issues/1328) al introducir el campo de configuración `sessions.Config.Driver` que se establece de forma predeterminada en `Redigo()` pero también se puede establecer en `Radix()`, futuras adiciones son bienvenidas. - -Registro de commits: https://github.com/kataras/iris/compare/v11.2.3...v11.2.4 - -# Martes, 30 de julio 2019 | v11.2.3 - -- [Nueva característica: Manejar diferentes tipos de parámetros en la misma ruta](https://github.com/kataras/iris/issues/1315) -- [Nueva característica: Negociación de contenido](https://github.com/kataras/iris/issues/1319) -- [Context.ReadYAML](https://github.com/kataras/iris/tree/master/_examples/request-body/read-yaml) -- Ajustes https://github.com/kataras/neffos/issues/1#issuecomment-515698536 - -# Miércoles, 24 de julio 2019 | v11.2.2 - -Sesiones como middleware: - -```go -import "github.com/kataras/iris/v12/sessions" -// [...] - -app := iris.New() -sess := sessions.New(sessions.Config{...}) - -app.Get("/path", func(ctx iris.Context){ - session := sessions.Get(ctx) - // [work with session...] -}) -``` - -- Agregado `Session.Len() int` para devolver el número total de valores/entradas almacenados. -- Permitir que `Context.HTML` y `Context.Text` acepten tambien un argumento `args ...interface{}` opcional y variable. - -## v11.1.1 - -- https://github.com/kataras/iris/issues/1298 -- https://github.com/kataras/iris/issues/1207 - -# Martes, 23 de julio 2019 | v11.2.0 - -Lea sobre la nueva versión liberada en: https://www.facebook.com/iris.framework/posts/3276606095684693 diff --git a/README.md b/README.md index 5a282c62ea..f9915d9222 100644 --- a/README.md +++ b/README.md @@ -1,236 +1,3 @@ -[![Black Lives Matter](https://iris-go.com/images/blacklivesmatter_banner.png)](https://support.eji.org/give/153413/#!/donation/checkout) +# Iris Web Framework - - -> This is the under-**development branch**. Stay tuned for the upcoming release [v12.2.0](HISTORY.md#Next). Looking for a stable release? Head over to the [v12.1.8 branch](https://github.com/kataras/iris/tree/v12.1.8) instead. -> -> ![](https://iris-go.com/images/cli.png) Try the official [Iris Command Line Interface](https://github.com/kataras/iris-cli) today! - - - -# Iris Web Framework - -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![view examples](https://img.shields.io/badge/examples%20-173-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate) - - - -Iris is a fast, simple yet fully featured and very efficient web framework for Go. - -It provides a beautifully expressive and easy to use foundation for your next website or API. - -Learn what [others saying about Iris](https://iris-go.com/testimonials/) and **[star](https://github.com/kataras/iris/stargazers)** this open-source project to support its potentials. - -[![](https://iris-go.com/images/reviews.gif)](https://iris-go.com/testimonials/) - -[![Benchmarks: Jul 18, 2020 at 10:46am (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks) - -## 👑 Supporters - -

- 陆 轶丰 - Weihang Ding - Li Fang - TechMaster - lenses.io - Celso Souza - Altafino - Thomas Fritz - Conrad Steenberg - Damon Zhao - George Opritescu - Juanses - Ankur Srivastava - Lex Tang - li3p -

- -## 📖 Learning Iris - -```sh -# https://github.com/kataras/iris/wiki/Installation -$ go get github.com/kataras/iris/v12@master -# assume the following code in main.go file -$ cat main.go -``` - -```go -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - - booksAPI := app.Party("/books") - { - booksAPI.Use(iris.Compression) - - // GET: http://localhost:8080/books - booksAPI.Get("/", list) - // POST: http://localhost:8080/books - booksAPI.Post("/", create) - } - - app.Listen(":8080") -} - -// Book example. -type Book struct { - Title string `json:"title"` -} - -func list(ctx iris.Context) { - books := []Book{ - {"Mastering Concurrency in Go"}, - {"Go Design Patterns"}, - {"Black Hat Go"}, - } - - ctx.JSON(books) - // TIP: negotiate the response between server's prioritizes - // and client's requirements, instead of ctx.JSON: - // ctx.Negotiation().JSON().MsgPack().Protobuf() - // ctx.Negotiate(books) -} - -func create(ctx iris.Context) { - var b Book - err := ctx.ReadJSON(&b) - // TIP: use ctx.ReadBody(&b) to bind - // any type of incoming data instead. - if err != nil { - ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem(). - Title("Book creation failure").DetailErr(err)) - // TIP: use ctx.StopWithError(code, err) when only - // plain text responses are expected on errors. - return - } - - println("Received Book: " + b.Title) - - ctx.StatusCode(iris.StatusCreated) -} -``` - -**MVC** equivalent: - -```go -import "github.com/kataras/iris/v12/mvc" -``` - -```go -m := mvc.New(booksAPI) -m.Handle(new(BookController)) -``` - -```go -type BookController struct { - /* dependencies */ -} - -// GET: http://localhost:8080/books -func (c *BookController) Get() []Book { - return []Book{ - {"Mastering Concurrency in Go"}, - {"Go Design Patterns"}, - {"Black Hat Go"}, - } -} - -// POST: http://localhost:8080/books -func (c *BookController) Post(b Book) int { - println("Received Book: " + b.Title) - - return iris.StatusCreated -} -``` - -**Run** your Iris web server: - -```sh -$ go run main.go -> Now listening on: http://localhost:8080 -> Application started. Press CTRL+C to shut down. -``` - -**List** Books: - -```sh -$ curl --header 'Accept-Encoding:gzip' http://localhost:8080/books - -[ - { - "title": "Mastering Concurrency in Go" - }, - { - "title": "Go Design Patterns" - }, - { - "title": "Black Hat Go" - } -] -``` - -**Create** a new Book: - -```sh -$ curl -i -X POST \ ---header 'Content-Encoding:gzip' \ ---header 'Content-Type:application/json' \ ---data "{\"title\":\"Writing An Interpreter In Go\"}" \ -http://localhost:8080/books - -> HTTP/1.1 201 Created -``` - -That's how an **error** response looks like: - -```sh -$ curl -X POST --data "{\"title\" \"not valid one\"}" \ -http://localhost:8080/books - -> HTTP/1.1 400 Bad Request - -{ - "status": 400, - "title": "Book creation failure" - "detail": "invalid character '\"' after object key", -} -``` - - - -[![run in the browser](https://img.shields.io/badge/Run-in%20the%20Browser-348798.svg?style=for-the-badge&logo=repl.it)](https://bit.ly/2YJeSZe) - -Iris contains extensive and thorough **[wiki](https://github.com/kataras/iris/wiki)** making it easy to get started with the framework. - - - -For a more detailed technical documentation you can head over to our [godocs](https://godoc.org/github.com/kataras/iris). And for executable code you can always visit the [./_examples](_examples) repository's subdirectory. - -### Do you like to read while traveling? - - Book cover - -[![follow Iris web framework on twitter](https://img.shields.io/twitter/follow/iris_framework?color=ee7506&logoColor=ee7506&style=for-the-badge)](https://twitter.com/intent/follow?screen_name=iris_framework) - -You can [request](https://iris-go.com/#book) a PDF version and online access of the **E-Book** today and be participated in the development of Iris. - -## 🙌 Contributing - -We'd love to see your contribution to the Iris Web Framework! For more information about contributing to the Iris project please check the [CONTRIBUTING.md](CONTRIBUTING.md) file. - -[List of all Contributors](https://github.com/kataras/iris/graphs/contributors) - -## 🛡 Security Vulnerabilities - -If you discover a security vulnerability within Iris, please send an e-mail to [iris-go@outlook.com](mailto:iris-go@outlook.com). All security vulnerabilities will be promptly addressed. - -## 📝 License - -This project is licensed under the [BSD 3-clause license](LICENSE), just like the Go project itself. - -The project name "Iris" was inspired by the Greek mythology. - +Stale Release for pre-go modules. Please follow instead. diff --git a/README_ES.md b/README_ES.md deleted file mode 100644 index fec5f5e9bd..0000000000 --- a/README_ES.md +++ /dev/null @@ -1,76 +0,0 @@ -# Iris Web Framework - -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) - -Iris es un framework web rápido, simple pero con muchas funcionalidades y muy eficiente para Go. Proporciona una base bellamente expresiva y fácil de usar para su próximo sitio web o API. - -Descubra lo que [otros dicen sobre Iris](https://iris-go.com/testimonials/) y **siga** :star: este repositorio github. - -[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/) - -[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks) - -## Aprende Iris - -
-Inicio rapido - -```sh -# agrega el siguiente código en el archivo ejemplo.go -$ cat ejemplo.go -``` - -```go -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.Default() - app.Get("/ping", func(ctx iris.Context) { - ctx.JSON(iris.Map{ - "message": "pong", - }) - }) - - app.Listen(":8080") -} -``` - -```sh -# ejecuta ejemplo.go y -# visita http://localhost:8080/ping en el navegador -$ go run ejemplo.go -``` - -> El enrutamiento es impulsado por [muxie](https://github.com/kataras/muxie), el software basado en trie más potente y rápido escrito en Go. - -
- -Iris contiene un extenso y completo **[wiki](https://github.com/kataras/iris/wiki)** que facilita comenzar con el framework. - -Para obtener una documentación técnica más detallada, puede dirigirse a nuestros [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0). Y para código ejecutable siempre puede visitar el subdirectorio del repositorio [\_examples](_examples/). - -### ¿Te gusta leer mientras viajas? - - Book cover - -[![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos) - -Puedes [solicitar](https://bit.ly/iris-req-book) una versión en PDF y acceso en línea del **E-Book** hoy y participar en el desarrollo de Iris. - -## Contribuir - -¡Nos encantaría ver su contribución al Framework Web Iris! Para obtener más información sobre cómo contribuir al proyecto Iris, consulte el archivo [CONTRIBUTING.md](CONTRIBUTING). - -[Lista de todos los contribuyentes](https://github.com/kataras/iris/graphs/contributors) - -## Vulnerabilidades de seguridad - -Si descubres una vulnerabilidad de seguridad dentro de Iris, envíe un correo electrónico a [iris-go@outlook.com](mailto:iris-go@outlook.com). Todas las vulnerabilidades de seguridad serán tratadas de inmediato. - -## Licencia - -El nombre del proyecto "Iris" se inspiró en la mitología griega. - -El Web Framework Iris es un software gratuito y de código abierto con licencia bajo la [Licencia BSD 3 cláusulas](LICENSE). diff --git a/README_FA.md b/README_FA.md deleted file mode 100644 index dcdacaf8cd..0000000000 --- a/README_FA.md +++ /dev/null @@ -1,102 +0,0 @@ -# Iris Web Framework - -
- -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) - -
- -آیریس سریع ترین و ساده ترین و موثرترین فریمورک وب در زبان GO میباشد. آیریس ساختاری بسیار زیبا و کارآمد را فراهم کرده است تا شما از آن برای پروژه های بعدی تان استفاده کنید. . - -برای این که بدانید دیگران در مورد آیریس چه می گویند لطفا در این لینک کلیک کنید [دیگران در مورد آیریس چه می گویند](https://iris-go.com/testimonials/) لطفا این پروژه را در گیتاب **استار** کنید. - -[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/) - -[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks) - -## آموزش آیریس - -
-شروع سریع - -
- -
- -```sh - -# فرض کنید همچین کدی را در فایل example.go نوشته اید -``` - -
- -```sh -$ cat example.go -``` - -```go -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.Default() - app.Get("/ping", func(ctx iris.Context) { - ctx.JSON(iris.Map{ - "message": "pong", - }) - }) - - app.Listen(":8080") -} -``` - -```sh -# run example.go and -# visit http://localhost:8080/ping on browser -$ go run example.go -``` - -
- -
- -> ایریس از پروژه ی [muxie](https://github.com/kataras/muxie) که موثرترین و سریع ترین پروژه مسیریابی در GO می باشد استفاده می کند. - -
- -
- -آیریس داری **[wiki](https://github.com/kataras/iris/wiki)** بسیار کامل و گسترده ای میباشد که یادگیری ان را ساده می کند. - -شما برای مشاهده و خواندن داکیومنت های فنی میتوانید به [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0) مراجعه کنید و همچنین برای مشاهده مثال ها و کد های قابل اجرا همیشه میتوانید از [مثال ها](_examples/) استفاده کنید . - -### آیا شما مطالعه کردن در طول سفر را دوست دارید ؟ - -
- - Book cover - -
- -[![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos) - -شما میتوانید در خواست یک نسخه PDF داکیومنت ر ا به صورت رایگان از اینجا بدهید [درخواست](https://bit.ly/iris-req-book) - -## مشارکت کردن - -ما دوست داریم که شما در فریمورک آیریس مشارکت کنید و کد ها را توسعه و بهبود ببخشید ! برای اطلاع بیشتر در مورد نحوه ی مشارکت کردن در این پروژه لطفا اینجا را بررسی کنید [CONTRIBUTING.md](CONTRIBUTING.md) - -[مشاهده ی همه ی مشارکت کننده ها](https://github.com/kataras/iris/graphs/contributors) - -## باگ های امنیتی - -اگر شما باگ های امنیتی در آیریس پیدا کردید لطفا یک ایمیل به [iris-go@outlook.com](mailto:iris-go@outlook.com) ارسال کنید. همه ی باگ های امنیتی بلافاصله برطرف میشود. - -## مجوز - -نام پروژه آیریس ریشه ای یونانی دارد. - -فریمورک آیریس رایگان و سورس باز و تحت مجوز [3-Clause BSD License](LICENSE) می باشد. - -
diff --git a/README_FR.md b/README_FR.md deleted file mode 100644 index 1ab7394a73..0000000000 --- a/README_FR.md +++ /dev/null @@ -1,87 +0,0 @@ -# Iris Web Framework - -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) - - - -Iris est un framework open-source pour Go à la fois simple, rapide et pourvu de nombreuses fonctionnalités. - -Il fournit des moyens simples et élégants de construire les bases et fonctionnalités de votre site, application backend ou API Rest. - -Lisez [ce que les développeurs pensent d'Iris](https://iris-go.com/testimonials/) et si l'envie vous prend **[étoilez](https://github.com/kataras/iris/stargazers)** le projet pour faire monter son potentiel. - -[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/) - -[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks) - -## 📖 Démarrer avec Iris - -
-Un simple Hello World - -```sh -# https://github.com/kataras/iris/wiki/Installation -$ go get github.com/kataras/iris/v12@latest -# assume the following code in example.go file -$ cat example.go -``` - -```go -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - app.Get("/ping", func(ctx iris.Context) { - ctx.JSON(iris.Map{ - "message": "pong", - }) - }) - - app.Listen(":8080") // port d'écoute -} -``` - -```sh -# compile et execute example.go -$ go run example.go -# maintenant visitez http://localhost:8080/ping -``` - -> Le routing est géré par [muxie](https://github.com/kataras/muxie), la librairie Go la plus rapide et complète. - -
- -Iris possède un **[wiki](https://github.com/kataras/iris/wiki)** complet et précis qui vous permettra d'implémenter ses fonctionnalités rapidement et facilement. - - - -Pour une documentation encore plus complète vous pouvez visiter notre [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0) (en Anglais). Et vous trouverez du code executable dans le dossier [\_examples](_examples/). - -### Vous préférez une version PDF? - - Book cover - -[![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos) - -Vous pouvez [demander](https://bit.ly/iris-req-book) une version **E-Book** (en Anglais) de la documentation et contribuer au développement d'Iris. - -## 🙌 Contribuer - -Toute contribution à Iris est la bienvenue ! Pour plus d'informations sur la contribution au projet référez-vous au fichier [CONTRIBUTING.md](CONTRIBUTING.md). - -[Liste des contributeurs](https://github.com/kataras/iris/graphs/contributors) - -## 🛡 Sécurité et vulnérabilités - -Si vous trouvez une vulnérabilité dans Iris, envoyez un e-mail à [iris-go@outlook.com](mailto:iris-go@outlook.com). Toute vulnérabilité sera corrigée aussi rapidement que possible. - -## 📝 Licence - -Le projet est sous licence [licence BSD 3](LICENSE), tout comme le langage Go lui même. - -Le nom "Iris" est inspiré de la mythologie Grecque. - diff --git a/README_GR.md b/README_GR.md deleted file mode 100644 index b7d98ff6ea..0000000000 --- a/README_GR.md +++ /dev/null @@ -1,80 +0,0 @@ -# Iris Web Framework - -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) - -Το Iris είναι ένα γρήγορο, απλό αλλά και πλήρως λειτουργικό και πολύ αποδοτικό web framework για τη Go γλώσσα προγραμματισμού. Παρέχει ένα εκφραστικό και εύχρηστο υπόβαθρο για την επόμενη ιστοσελίδα σας. - -Μάθετε τι [λένε οι άλλοι για το Iris](https://iris-go.com/testimonials/) και δώστε ένα **αστεράκι** στο GitHub. - -[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/) - -[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks) - -## Μαθαίνοντας το Iris - -
-Γρήγορο ξεκίνημα - -```sh -# υποθέτοντας ότι ο παρακάτω κώδικας -# βρίσκεται στο example.go αρχείο -# -$ cat example.go -``` - -```go -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.Default() - app.Get("/ping", func(ctx iris.Context) { - ctx.JSON(iris.Map{ - "message": "pong", - }) - }) - - app.Listen(":8080") -} -``` - -```sh -# τρέξτε το example.go και -# επισκεφτείτε την σελίδα http://localhost:8080/ping -# στο πρόγραμμα περιήγησης σας -# -$ go run example.go -``` - -> Η δρομολόγηση τροφοδοτείται από το [muxie](https://github.com/kataras/muxie), το πιο ισχυρό και ταχύτερο λογισμικό βασισμένο σε trie αλγόριθμο που γράφτηκε σε Go. - -
- -Το Iris περιέχει εκτενείς και λεπτομερείς **[wiki](https://github.com/kataras/iris/wiki)** καθιστώντας το εύκολο στην εκμάθηση. - -Για λεπτομερέστερη τεχνική τεκμηρίωση μπορείτε να κατευθυνθείτε προς τα [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0) μας. Και για εκτελέσιμο κώδικα μπορείτε πάντα να επισκέπτεστε τα [παραδείγματα](_examples/). - -### Σας αρέσει να διαβάζετε ενώ ταξιδεύετε; - -Μπορείτε να [ζητήσετε](https://bit.ly/iris-req-book) σήμερα την PDF έκδοση και την online πρόσβαση στο Ηλεκτρονικό μας **Βιβλίο(E-Book)** και να συμμετάσχετε στην ανάπτυξη του Iris. - -[![https://iris-go.com/images/iris-book-cover-sm.jpg](https://iris-go.com/images/iris-book-cover-sm.jpg)](https://bit.ly/iris-req-book) - -[![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos) - -## Συνεισφορά - -Θα θέλαμε να δούμε τη συμβολή σας στο Iris Web Framework! Για περισσότερες πληροφορίες σχετικά με το πως μπορείτε να συμβάλετε, δείτε το [CONTRIBUTING.md](CONTRIBUTING.md) αρχείο. - -[Κατάλογος όλων των συνεισφορών](https://github.com/kataras/iris/graphs/contributors). - -## Αδυναμίες Ασφάλειας - -Εάν εντοπίσετε κάποια αδυναμία ασφαλείας του Iris, στείλτε ένα μήνυμα ηλεκτρονικού ταχυδρομείου στο [iris-go@outlook.com](mailto:iris-go@outlook.com). Όλες οι τυχών αδυναμίες ασφαλείας θα αντιμετωπιστούν άμεσα. - -## Άδεια Χρήσης - -Το όνομα "Iris" εμπνεύστηκε από την ελληνική μυθολογία, συγκεκριμένα από την θεά Ίριδα. - -Το Iris Web Framework είναι δωρεάν λογισμικό ανοιχτού λογισμικού με άδεια χρήσης [3-Clause BSD](LICENSE). diff --git a/README_KO.md b/README_KO.md deleted file mode 100644 index 97bd485bbe..0000000000 --- a/README_KO.md +++ /dev/null @@ -1,76 +0,0 @@ -# Iris Web Framework - -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) - -Iris는 단순하고 빠르며 좋은 성능과 모든 기능을 갖춘 Go언어용 웹 프레임워크입니다. 당신의 웹사이트나 API를 위해서 아름답고 사용하기 쉬운 기반을 제공합니다. - -[여러 사람들의 의견](https://iris-go.com/testimonials/)을 둘러보세요. 그리고 이 github repository을 **star**하세요. - -[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/) - -[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks) - -## Iris 배우기 - -
-일단 해보기 - -```sh -# 다음 코드를 example.go 화일에 입력하세요. -$ cat example.go -``` - -```go -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.Default() - app.Get("/ping", func(ctx iris.Context) { - ctx.JSON(iris.Map{ - "message": "pong", - }) - }) - - app.Listen(":8080") -} -``` - -```sh -# example.go 를 실행하고, -# 웹브라우저에서 http://localhost:8080/ping 를 열어보세요. -$ go run example.go -``` - -> 라우팅은 Go로 작성한 가장 강력하고 빠른 trie기반의 소프트웨어인 [muxie](https://github.com/kataras/muxie)로 처리합니다. - -
- -Iris는 광범위하고 꼼꼼한 **[wiki](https://github.com/kataras/iris/wiki)** 를 가지고 있기 때문에 쉽게 프레임워크를 시작할 수 있습니다. - -더 자세한 기술문서를 보시려면 [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0)를 방문하세요. 그리고 실행가능한 예제코드는 [\_examples](_examples/) 하위 디렉토리에 있습니다. - -### 여행하면서 독서를 즐기세요? - - Book cover - -[![follow author](https://img.shields.io/twitter/follow/makismaropoulos.svg?style=for-the-badge)](https://twitter.com/intent/follow?screen_name=makismaropoulos) - -PDF 버전과 **E-Book** 에 대한 온라인 접근을 [요청](https://bit.ly/iris-req-book)하시고 Iris의 개발에 참가하실 수 있습니다. - -## 기여하기 - -Iris 웹 프레임워크에 대한 여러분의 기여를 환영합니다! Iris 프로젝트에 기여하는 방법에 대한 자세한 내용은 [CONTRIBUTING.md](CONTRIBUTING.md) 파일을 참조하십시오. - -[기여자 리스트](https://github.com/kataras/iris/graphs/contributors) - -## 보안 취약점 - -만약 Iris에서 보안 취약점을 발견하시면 [iris-go@outlook.com](mailto:iris-go@outlook.com) 로 메일을 보내주세요. 모든 보안 취약점은 즉 해결할 것입니다. - -## 라이센스 - -이 프로젝트의 이름 "Iris"는 그리스 신화에서 영감을 받았습니다. - -Iris 웹 프레임워크는 [3-Clause BSD License](LICENSE)를 가지는 무료 오픈소스 소프트웨어입니다. diff --git a/README_RU.md b/README_RU.md deleted file mode 100644 index 412456fb59..0000000000 --- a/README_RU.md +++ /dev/null @@ -1,81 +0,0 @@ -# Iris Web Framework - -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![FOSSA Status](https://img.shields.io/badge/LICENSE%20SCAN-PASSING❤️-CD2956?style=for-the-badge&logo=fossa)](https://app.fossa.io/projects/git%2Bgithub.com%2Fkataras%2Firis?ref=badge_shield) [![view examples](https://img.shields.io/badge/learn%20by-examples-0C8EC5.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=7E18DD&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) -Iris — это быстрый, простой, но полнофункциональный и эффективный веб-фреймворк для Go. Он обеспечивает красивую, выразительную и простую в использовании основу для вашего следующего веб-сайта или API. - -Узнайте, что [говорят другие люди об Iris](https://iris-go.com/testimonials/) и поставьте **[звёздочку](https://github.com/kataras/iris/stargazers)** этому проекту с открытым исходным кодом, чтобы поддержать его потенциал. - -[![](https://media.giphy.com/media/j5WLmtvwn98VPrm7li/giphy.gif)](https://iris-go.com/testimonials/) - -[![Benchmarks: Apr 2, 2020 at 12:13pm (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks) - -## Изучение Iris - -
-Быстрый старт - -```sh -# например, код в файле example.go будет таким: -$ cat example.go -``` - -```go -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.Default() - app.Get("/ping", func(ctx iris.Context) { - ctx.JSON(iris.Map{ - "message": "pong", - }) - }) - - app.Listen(":8080") -} -``` - -```sh -# запустите example.go и перейдите в браузер -# по адресу http://localhost:8080/ping -$ go run example.go -``` - -> Система роутинга запросов работает на [muxie](https://github.com/kataras/muxie), мощное и быстрое trie-based ПО, написанное на Go. - -
- -У Iris есть исчерпывающий и тщательный **[wiki](https://github.com/kataras/iris/wiki)**, который позволит вам быстрее начать работу с фреймворком. - - - -Для получения более подробной технической документации вы можете обратиться к нашему [godoc](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0). А для живых примеров кода — вы всегда можете посетить [\_examples](_examples/) в поддиректории этого репозитория. - -### Вы любите читать во время путешествий? - - Book cover - - - -Вы можете [запросить](https://bit.ly/iris-req-book) PDF версию и онлайн-доступ к **E-Book** сегодня и принять участие в разработке Iris. - -## Содействие - -Мы будем рады видеть ваш вклад в веб-фреймворк Iris! Для получения дополнительной информации о содействии проекту Iris, пожалуйста, проверьте файл [CONTRIBUTING.md](CONTRIBUTING.md). - -[Список всех участников](https://github.com/kataras/iris/graphs/contributors) - -## Уязвимость безопасности - -Если вы обнаружите уязвимость безопасности в Iris, отправьте электронное письмо по адресу [iris-go@outlook.com](mailto:iris-go@outlook.com). Все уязвимости безопасности будут оперативно устранены. - -## Лицензия - -Название проекта «Iris» было вдохновлено греческой мифологией. - -Веб-фреймворк Iris — это ПО с открытым исходным кодом под лицензией [3-Clause BSD License](LICENSE). - -## Накопление звёзд со временем - -[![Stargazers over time](https://starchart.cc/kataras/iris.svg)](https://starchart.cc/kataras/iris) diff --git a/README_ZH.md b/README_ZH.md deleted file mode 100644 index 4494aa5efa..0000000000 --- a/README_ZH.md +++ /dev/null @@ -1,216 +0,0 @@ -[![黑人的命也是命](https://iris-go.com/images/blacklivesmatter_banner.png)](https://support.eji.org/give/153413/#!/donation/checkout) - - - -> 这是一个**开发中的版本**。敬请关注即将发布的版本 [v12.2.0](HISTORY.md#Next)。如果想使用稳定版本,请查看 [v12.1.8 分支](https://github.com/kataras/iris/tree/v12.1.8) 。 -> -> ![](https://iris-go.com/images/cli.png) 立即尝试官方的[Iris命令行工具](https://github.com/kataras/iris-cli)! - - - -# Iris Web Framework - -[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=for-the-badge&logo=travis)](https://travis-ci.org/kataras/iris) [![view examples](https://img.shields.io/badge/examples%20-173-a83adf.svg?style=for-the-badge&logo=go)](https://github.com/kataras/iris/tree/master/_examples) [![chat](https://img.shields.io/gitter/room/iris_go/community.svg?color=cc2b5e&logo=gitter&style=for-the-badge)](https://gitter.im/iris_go/community) [![donate](https://img.shields.io/badge/support-Iris-blue.svg?style=for-the-badge&logo=paypal)](https://iris-go.com/donate) - - - -Iris 是基于 Go 编写的一个快速,简单但功能齐全且非常高效的 Web 框架。 - -它为您的下一个网站或 API 提供了一个非常富有表现力且易于使用的基础。 - -看看 [其他人如何评价 Iris](https://iris-go.com/testimonials/),同时欢迎各位为此开源项目点亮 **[star](https://github.com/kataras/iris/stargazers)**。 - -[![](https://iris-go.com/images/reviews.gif)](https://iris-go.com/testimonials/) - -[![Benchmarks: Jul 18, 2020 at 10:46am (UTC)](https://iris-go.com/images/benchmarks.svg)](https://github.com/kataras/server-benchmarks) - -## 📖 开始学习 Iris - -```sh -# 安装Iris:https://github.com/kataras/iris/wiki/Installation -$ go get github.com/kataras/iris/v12@master -# 假设main.go文件中已存在以下代码 -$ cat main.go -``` - -```go -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - - booksAPI := app.Party("/books") - { - booksAPI.Use(iris.Compression) - - // GET: http://localhost:8080/books - booksAPI.Get("/", list) - // POST: http://localhost:8080/books - booksAPI.Post("/", create) - } - - app.Listen(":8080") -} - -// Book example. -type Book struct { - Title string `json:"title"` -} - -func list(ctx iris.Context) { - books := []Book{ - {"Mastering Concurrency in Go"}, - {"Go Design Patterns"}, - {"Black Hat Go"}, - } - - ctx.JSON(books) - // 提示: 在服务器优先级和客户端请求中进行响应协商, - // 以此来代替 ctx.JSON: - // ctx.Negotiation().JSON().MsgPack().Protobuf() - // ctx.Negotiate(books) -} - -func create(ctx iris.Context) { - var b Book - err := ctx.ReadJSON(&b) - // 提示: 使用 ctx.ReadBody(&b) 代替,来绑定所有类型的入参 - if err != nil { - ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem(). - Title("Book creation failure").DetailErr(err)) - // 提示: 如果仅有纯文本(plain text)错误响应, - // 可使用 ctx.StopWithError(code, err) - return - } - - println("Received Book: " + b.Title) - - ctx.StatusCode(iris.StatusCreated) -} -``` - -同样地,在**MVC**中 : - -```go -import "github.com/kataras/iris/v12/mvc" -``` - -```go -m := mvc.New(booksAPI) -m.Handle(new(BookController)) -``` - -```go -type BookController struct { - /* dependencies */ -} - -// GET: http://localhost:8080/books -func (c *BookController) Get() []Book { - return []Book{ - {"Mastering Concurrency in Go"}, - {"Go Design Patterns"}, - {"Black Hat Go"}, - } -} - -// POST: http://localhost:8080/books -func (c *BookController) Post(b Book) int { - println("Received Book: " + b.Title) - - return iris.StatusCreated -} -``` - -**启动** 您的 Iris web 服务: - -```sh -$ go run main.go -> Now listening on: http://localhost:8080 -> Application started. Press CTRL+C to shut down. -``` - -Books **列表查询** : - -```sh -$ curl --header 'Accept-Encoding:gzip' http://localhost:8080/books - -[ - { - "title": "Mastering Concurrency in Go" - }, - { - "title": "Go Design Patterns" - }, - { - "title": "Black Hat Go" - } -] -``` - -**创建** 新的Book: - -```sh -$ curl -i -X POST \ ---header 'Content-Encoding:gzip' \ ---header 'Content-Type:application/json' \ ---data "{\"title\":\"Writing An Interpreter In Go\"}" \ -http://localhost:8080/books - -> HTTP/1.1 201 Created -``` - -这是**错误**响应所展示的样子: - -```sh -$ curl -X POST --data "{\"title\" \"not valid one\"}" \ -http://localhost:8080/books - -> HTTP/1.1 400 Bad Request - -{ - "status": 400, - "title": "Book creation failure" - "detail": "invalid character '\"' after object key", -} -``` - - - -[![run in the browser](https://img.shields.io/badge/Run-in%20the%20Browser-348798.svg?style=for-the-badge&logo=repl.it)](https://bit.ly/2YJeSZe) - -Iris 有完整且详尽的 **[使用文档](https://github.com/kataras/iris/wiki)** ,让您可以轻松地使用此框架。 - - - -要了解更详细的技术文档,请访问我们的 [godocs](https://godoc.org/github.com/kataras/iris)。如果想要寻找代码示例,您可以到仓库的 [./_examples](_examples) 子目录下获取。 - -### 你喜欢在旅行时阅读吗? - - Book cover - -[![follow Iris web framework on twitter](https://img.shields.io/twitter/follow/iris_framework?color=ee7506&logoColor=ee7506&style=for-the-badge)](https://twitter.com/intent/follow?screen_name=iris_framework) - -您可以[获取](https://bit.ly/iris-req-book)PDF版本或在线访问**电子图书**,并参与到Iris的开发中。 - -## 🙌 贡献 - -我们欢迎您为Iris框架做出贡献!想要知道如何为Iris项目做贡献,请查看[CONTRIBUTING.md](CONTRIBUTING.md)。 - -[贡献者名单](https://github.com/kataras/iris/graphs/contributors) - -## 🛡 安全漏洞 - -如果您发现在 Iris 存在安全漏洞,请发送电子邮件至 [iris-go@outlook.com](mailto:iris-go@outlook.com)。所有安全漏洞将会得到及时解决。 - -## 📝 开源协议(License) - -就像Go语言的协议一样,此项目也采用 [BSD 3-clause license](LICENSE)。 - -项目名称 "Iris" 的灵感来自于希腊神话。 - - diff --git a/VERSION b/VERSION deleted file mode 100644 index b56d45d59d..0000000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -12.1.8:https://github.com/kataras/iris/releases/tag/v12.1.8 \ No newline at end of file diff --git a/_examples/README.md b/_examples/README.md deleted file mode 100644 index 536a756787..0000000000 --- a/_examples/README.md +++ /dev/null @@ -1,219 +0,0 @@ -# Table of Contents - -* [REST API for Apache Kafka](kafka-api) -* [URL Shortener](url-shortener) -* [Dropzone.js](dropzonejs) -* [Caddy](caddy) -* Database - * [MySQL, Groupcache & Docker](database/mysql) - * [MongoDB](database/mongodb) - * [Xorm](database/orm/xorm/main.go) - * [Gorm](database/orm/gorm/main.go) - * [Reform](database/orm/reform/main.go) -* HTTP Server - * [HOST:PORT](http-server/listen-addr/main.go) - * [Public Test Domain](http-server/listen-addr-public/main.go) - * [UNIX socket file](http-server/listen-unix/main.go) - * [TLS](http-server/listen-tls/main.go) - * [Letsencrypt (Automatic Certifications)](http-server/listen-letsencrypt/main.go) - * [Socket Sharding (SO_REUSEPORT)](http-server/socket-sharding/main.go) - * [Graceful Shutdown](http-server/graceful-shutdown/default-notifier/main.go) - * [Notify on shutdown](http-server/notify-on-shutdown/main.go) - * Custom TCP Listener - * [Common net.Listener](http-server/custom-listener/main.go) - * Custom HTTP Server - * [Pass a custom Server](http-server/custom-httpserver/easy-way/main.go) - * [Use Iris as a single http.Handler](http-server/custom-httpserver/std-way/main.go) - * [Multi Instances](http-server/custom-httpserver/multi/main.go) - * [HTTP/3 Quic](http-server/http3-quic) -* Configuration - * [Functional](configuration/functional/main.go) - * [Configuration Struct](configuration/from-configuration-structure/main.go) - * [Import from YAML](configuration/from-yaml-file/main.go) - * [Share Configuration across instances](configuration/from-yaml-file/shared-configuration/main.go) - * [Import from TOML](configuration/from-toml-file/main.go) -* Routing - * [Overview](routing/overview/main.go) - * [Basic](routing/basic/main.go) - * [Custom HTTP Errors](routing/http-errors/main.go) - * [Not Found - Intelligence](routing/intelligence/main.go) - * [Not Found - Suggest Closest Paths](routing/intelligence/manual/main.go) - * [Dynamic Path](routing/dynamic-path/main.go) - * [Root Wildcard](routing/dynamic-path/root-wildcard/main.go) - * [Implement a Parameter Type](routing/macros/main.go) - * [Same Path Pattern but Func](routing/dynamic-path/same-pattern-different-func/main.go) - * Middleware - * [Per Route](routing/writing-a-middleware/per-route/main.go) - * [Globally](routing/writing-a-middleware/globally/main.go) - * [Handlers Execution Rule](routing/route-handlers-execution-rules/main.go) - * [Route Register Rule](routing/route-register-rule/main.go) - * Convert net/http Handlers - * [From func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)](convert-handlers/negroni-like/main.go) - * [From http.Handler or http.HandlerFunc](convert-handlers/nethttp/main.go) - * [From func(http.HandlerFunc) http.HandlerFunc](convert-handlers/real-usecase-raven/writing-middleware/main.go) - * [Route State](routing/route-state/main.go) - * [Reverse Routing](routing/reverse/main.go) - * [Router Wrapper](routing/custom-wrapper/main.go) - * [Custom Router](routing/custom-router/main.go) - * Subdomains - * [Single](routing/subdomains/single/main.go) - * [Multi](routing/subdomains/multi/main.go) - * [Wildcard](routing/subdomains/wildcard/main.go) - * [WWW](routing/subdomains/www/main.go) - * [Redirection](routing/subdomains/redirect/main.go) - * [HTTP Method Override](https://github.com/kataras/iris/blob/master/middleware/methodoverride/methodoverride_test.go) - * [API Versioning](routing/versioning/main.go) - * [Sitemap](routing/sitemap/main.go) -* Logging - * [Request Logger](logging/request-logger/main.go) - * [Log Requests to a File](logging/request-logger/request-logger-file/main.go) - * [Log Requests to a JSON File](logging/request-logger/request-logger-file-json/main.go) - * [Application File Logger](logging/file-logger/main.go) - * [Application JSON Logger](logging/json-logger/main.go) - * [Rollbar](logging/rollbar/main.go) -* API Documentation - * [Yaag](apidoc/yaag/main.go) - * [Swagger](https://github.com/iris-contrib/swagger/tree/master/example) -* [Testing](testing/httptest/main_test.go) -* [Recovery](recover/main.go) -* [Profiling](pprof/main.go) -* File Server - * [File Server](file-server/file-server/main.go) - * [HTTP/2 Push Targets](file-server/http2push/main.go) - * [HTTP/2 Push Targets (Embedded)](file-server/http2push-embedded/main.go) - * [HTTP/2 Push Targets (Gzipped Embedded)](file-server/http2push-embedded-gzipped/main.go) - * [Favicon](file-server/favicon/main.go) - * [Basic](file-server/basic/main.go) - * [Embedding Files Into App Executable File](file-server/embedding-files-into-app/main.go) - * [Embedding Gzipped Files Into App Executable File](file-server/embedding-gzipped-files-into-app/main.go) - * [Send Files (rate limiter included)](file-server/send-files/main.go) - * Single Page Applications - * [Basic SPA](file-server/single-page-application/basic/main.go) - * [Embedded Single Page Application](file-server/single-page-application/embedded-single-page-application/main.go) - * [Embedded Single Page Application with other routes](file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go) - * [Upload File](file-server/upload-file/main.go) - * [Upload Multiple Files](file-server/upload-files/main.go) -* View - * [Overview](view/overview/main.go) - * [Basic](view/template_html_0/main.go) - * [A simple Layout](view/template_html_1/main.go) - * [Layouts: `yield` and `render` tmpl funcs](view/template_html_2/main.go) - * [The `urlpath` tmpl func](view/template_html_3/main.go) - * [The `url` tmpl func](view/template_html_4/main.go) - * [Inject Data Between Handlers](view/context-view-data/main.go) - * [Inject Engine Between Handlers](view/context-view-engine/main.go) - * [Embedding Templates Into App Executable File](view/embedding-templates-into-app/main.go) - * [Write to a custom `io.Writer`](view/write-to) - * [Blocks](view/template_blocks_0) - * [Blocks Embedded](view/template_blocks_1_embedded) - * [Pug: Greeting](view/template_pug_0) - * [Pug: `Actions`](view/template_pug_1) - * [Pug: `Includes`](view/template_pug_2) - * [Pug: `Extends`](view/template_pug_3) - * [Jet Template](view/template_jet_0) - * [Jet Embedded](view/template_jet_1_embedded) - * [Jet 'urlpath' tmpl func](view/template_jet_2) - * [Jet Template Funcs from Struct](view/template_jet_3) - - [Ace](view/template_ace_0) - * Third-Parties - * [Render `valyala/quicktemplate` templates](view/quicktemplate) - * [Render `shiyanhui/hero` templates](view/herotemplate) -* [Request ID](https://github.com/kataras/iris/blob/master/middleware/requestid/requestid_test.go) -* [Request Rate Limit](request-ratelimit/main.go) -* [Request Referrer](request-referrer/main.go) -* [Webassembly](webassembly/main.go) -* Request Body - * [Bind JSON](request-body/read-json/main.go) - * * [Struct Validation](request-body/read-json-struct-validation/main.go) - * [Bind XML](request-body/read-xml/main.go) - * [Bind MsgPack](request-body/read-msgpack/main.go) - * [Bind YAML](request-body/read-yaml/main.go) - * [Bind Form](request-body/read-form/main.go) - * [Bind Query](request-body/read-query/main.go) - * [Bind Body](request-body/read-body/main.go) - * [Bind Custom per type](request-body/read-custom-per-type/main.go) - * [Bind Custom via Unmarshaler](request-body/read-custom-via-unmarshaler/main.go) - * [Bind Many times](request-body/read-many/main.go) -* Response Writer - * [Content Negotiation](response-writer/content-negotiation) - * [Text, Markdown, YAML, HTML, JSON, JSONP, Msgpack, XML and Binary](response-writer/write-rest/main.go) - * [Protocol Buffers](response-writer/protobuf/main.go) - * [HTTP/2 Server Push](response-writer/http2push/main.go) - * [Stream Writer](response-writer/stream-writer/main.go) - * [Transactions](response-writer/transactions/main.go) - * [SSE](response-writer/sse/main.go) - * [SSE (third-party package usage for server sent events)](response-writer/sse-third-party/main.go) - * Cache - * [Simple](response-writer/cache/simple/main.go) - * [Client-Side (304)](response-writer/cache/client-side/main.go) -* Compression - * [Server-Side](compression/main.go) - * [Client-Side](compression/client/main.go) - * [Client-Side (using Iris)](compress/client-using-iris/main.go) -* Localization and Internationalization - * [i18n](i18n/main.go) -* Authentication, Authorization & Bot Detection - * [Basic Authentication](auth/basicauth/main.go) - * [CORS](auth/cors) - * [JWT](auth/jwt/main.go) - * [JWT (community edition)](https://github.com/iris-contrib/middleware/tree/v12/jwt/_example/main.go) - * [OAUth2](auth/goth/main.go) - * [Manage Permissions](auth/permissions/main.go) - * [Google reCAPTCHA](auth/recaptcha/main.go) - * [hCaptcha](auth/hcaptcha/main.go) -* Cookies - * [Basic](cookies/basic/main.go) - * [Options](cookies/options/main.go) - * [Encode/Decode (with `securecookie`)](cookies/securecookie/main.go) -* Sessions - * [Overview: Config](sessions/overview/main.go) - * [Overview: Routes](sessions/overview/example/example.go) - * [Basic](sessions/basic/main.go) - * [Secure Cookie](sessions/securecookie/main.go) - * [Flash Messages](sessions/flash-messages/main.go) - * [Databases](sessions/database) - * [Badger](sessions/database/badger/main.go) - * [BoltDB](sessions/database/boltdb/main.go) - * [Redis](sessions/database/redis/main.go) -* Websocket - * [Gorilla FileWatch (3rd-party)](websocket/gorilla-filewatch/main.go) - * [Basic](websocket/basic) - * [Server](websocket/basic/server.go) - * [Go Client](websocket/basic/go-client/client.go) - * [Browser Client](websocket/basic/browser/index.html) - * [Browser NPM Client (browserify)](websocket/basic/browserify/app.js) - * [Native Messages](websocket/native-messages/main.go) - * [TLS](websocket/secure/README.md) - * [Online Visitors](websocket/online-visitors/main.go) -* Dependency Injection - * [Overview (Movies Service)](ependency-injection/overview/main.go) - * [Basic](dependency-injection/basic/main.go) - * [Middleware](dependency-injection/basic/middleware/main.go) - * [Sessions](dependency-injection/sessions/main.go) - * [Smart Contract](dependency-injection/smart-contract/main.go) - * [JWT](dependency-injection/jwt/main.go) - * [JWT (iris-contrib)](dependency-injection/jwt/contrib/main.go) - * [Register Dependency from Context](dependency-injection/context-register-dependency/main.go) -* MVC - * [Overview](mvc/overview) - * [Repository and Service layers](mvc/repository) - * [Hello world](mvc/hello-world/main.go) - * [Basic](mvc/basic/main.go) - * [Wildcard](mvc/basic/wildcard/main.go) - * [Singleton](mvc/singleton) - * [Regexp](mvc/regexp/main.go) - * [Session Controller](mvc/session-controller/main.go) - * [Authenticated Controller](mvc/authenticated-controller/main.go) - * [Versioned Controller](mvc/versioned-controller/main.go) - * [Websocket Controller](mvc/websocket) - * [Register Middleware](mvc/middleware) - * [gRPC](mvc/grpc-compatible) - * [Login (Repository and Service layers)](mvc/login) - * [Login (Single Responsibility)](mvc/login-mvc-single-responsibility) - * [Vue.js Todo App](mvc/vuejs-todo-mvc) -* [Bootstrapper](bootstrapper) -* Desktop Applications - * [The blink package](desktop/blink) - * [The lorca package](desktop/lorca) - * [The webview package](desktop/webview) -* Middlewares [(Community)](https://github.com/iris-contrib/middleware) diff --git a/_examples/apidoc/swagger/README.md b/_examples/apidoc/swagger/README.md deleted file mode 100644 index 08e23049eb..0000000000 --- a/_examples/apidoc/swagger/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Swagger 2.0 - -Visit https://github.com/iris-contrib/swagger instead. diff --git a/_examples/apidoc/yaag/go.mod b/_examples/apidoc/yaag/go.mod deleted file mode 100644 index e1c27c2f78..0000000000 --- a/_examples/apidoc/yaag/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/kataras/iris/_examples/apidoc/yaag - -go 1.15 - -require ( - github.com/betacraft/yaag v1.0.1-0.20200719063524-47d781406108 - github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd -) diff --git a/_examples/apidoc/yaag/main.go b/_examples/apidoc/yaag/main.go deleted file mode 100644 index a50b346c06..0000000000 --- a/_examples/apidoc/yaag/main.go +++ /dev/null @@ -1,55 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - - "github.com/betacraft/yaag/irisyaag" - "github.com/betacraft/yaag/yaag" -) - -type myXML struct { - Result string `xml:"result"` -} - -func main() { - app := iris.New() - - yaag.Init(&yaag.Config{ // <- IMPORTANT, init the middleware. - On: true, - DocTitle: "Iris", - DocPath: "apidoc.html", - BaseUrls: map[string]string{"Production": "", "Staging": ""}, - }) - app.Use(irisyaag.New()) // <- IMPORTANT, register the middleware. - - app.Get("/json", func(ctx iris.Context) { - ctx.JSON(iris.Map{"result": "Hello World!"}) - }) - - app.Get("/plain", func(ctx iris.Context) { - ctx.Text("Hello World!") - }) - - app.Get("/xml", func(ctx iris.Context) { - ctx.XML(myXML{Result: "Hello World!"}) - }) - - app.Get("/complex", func(ctx iris.Context) { - value := ctx.URLParam("key") - ctx.JSON(iris.Map{"value": value}) - }) - - // Run our HTTP Server. - // - // Documentation of "yaag" doesn't note the follow, but in Iris we are careful on what - // we provide to you. - // - // Each incoming request results on re-generation and update of the "apidoc.html" file. - // Recommentation: - // Write tests that calls those handlers, save the generated "apidoc.html". - // Turn off the yaag middleware when in production. - // - // Example usage: - // Visit all paths and open the generated "apidoc.html" file to see the API's automated docs. - app.Listen(":8080") -} diff --git a/_examples/auth/basicauth/main.go b/_examples/auth/basicauth/main.go deleted file mode 100644 index cfc279f00f..0000000000 --- a/_examples/auth/basicauth/main.go +++ /dev/null @@ -1,57 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/basicauth" -) - -func newApp() *iris.Application { - app := iris.New() - - authConfig := basicauth.Config{ - Users: map[string]string{"myusername": "mypassword", "mySecondusername": "mySecondpassword"}, - Realm: "Authorization Required", // defaults to "Authorization Required" - Expires: time.Duration(30) * time.Minute, - } - - authentication := basicauth.New(authConfig) - - // to global app.Use(authentication) (or app.UseGlobal before the .Run) - // to routes - /* - app.Get("/mysecret", authentication, h) - */ - - app.Get("/", func(ctx iris.Context) { ctx.Redirect("/admin") }) - - // to party - - needAuth := app.Party("/admin", authentication) - { - //http://localhost:8080/admin - needAuth.Get("/", h) - // http://localhost:8080/admin/profile - needAuth.Get("/profile", h) - - // http://localhost:8080/admin/settings - needAuth.Get("/settings", h) - } - - return app -} - -func main() { - app := newApp() - // open http://localhost:8080/admin - app.Listen(":8080") -} - -func h(ctx iris.Context) { - username, password, _ := ctx.Request().BasicAuth() - // third parameter it will be always true because the middleware - // makes sure for that, otherwise this handler will not be executed. - - ctx.Writef("%s %s:%s", ctx.Path(), username, password) -} diff --git a/_examples/auth/basicauth/main_test.go b/_examples/auth/basicauth/main_test.go deleted file mode 100644 index e3f27486ce..0000000000 --- a/_examples/auth/basicauth/main_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestBasicAuth(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - // redirects to /admin without basic auth - e.GET("/").Expect().Status(httptest.StatusUnauthorized) - // without basic auth - e.GET("/admin").Expect().Status(httptest.StatusUnauthorized) - - // with valid basic auth - e.GET("/admin").WithBasicAuth("myusername", "mypassword").Expect(). - Status(httptest.StatusOK).Body().Equal("/admin myusername:mypassword") - e.GET("/admin/profile").WithBasicAuth("myusername", "mypassword").Expect(). - Status(httptest.StatusOK).Body().Equal("/admin/profile myusername:mypassword") - e.GET("/admin/settings").WithBasicAuth("myusername", "mypassword").Expect(). - Status(httptest.StatusOK).Body().Equal("/admin/settings myusername:mypassword") - - // with invalid basic auth - e.GET("/admin/settings").WithBasicAuth("invalidusername", "invalidpassword"). - Expect().Status(httptest.StatusUnauthorized) -} diff --git a/_examples/auth/cors/main.go b/_examples/auth/cors/main.go deleted file mode 100644 index c5b6c5e8cd..0000000000 --- a/_examples/auth/cors/main.go +++ /dev/null @@ -1,63 +0,0 @@ -// Package main integrates the "rs/cors" net/http middleware into Iris. -// That cors third-party middleware cannot be registered through `iris.FromStd` -// as a common middleware because it should be injected before the Iris Router itself, -// it allows/dissallows HTTP Methods too. -// -// This is just an example you can use to run something, based on custom logic, -// before the Iris Router itself. -// -// In the "routing/custom-wrapper" example -// we learn how we can acquire and release an Iris context to fire an Iris Handler -// based on custom logic, before the Iris Router itself. In that example -// we will fire a net/http handler (the "rs/cors" handler one) instead. -// -// https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/rs/cors" -) - -func main() { - app := iris.New() - c := cors.New(cors.Options{ - AllowedOrigins: []string{"*"}, - AllowCredentials: true, - // Enable Debugging for testing, consider disabling in production - Debug: true, - }) - // app.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) { - // [custom logic...] - // if shouldFireNetHTTPHandler { - // ...ServeHTTP(w,r) - // return - // } - // router(w,r) - // }) - // In our case, the cors package has a ServeHTTP - // of the same form of app.WrapRouter's accept input argument, - // so we can just do: - app.WrapRouter(c.ServeHTTP) - - // Serve ./public/index.html, main.js. - app.HandleDir("/", iris.Dir("./public")) - - // Register routes here... - app.Get("/data", listData) - - // http://localhost:8080 and click the "fetch data" button. - app.Listen(":8080") -} - -type item struct { - Title string `json:"title"` -} - -func listData(ctx iris.Context) { - ctx.JSON([]item{ - {"Item 1"}, - {"Item 2"}, - {"Item 3"}, - }) -} diff --git a/_examples/auth/cors/public/index.html b/_examples/auth/cors/public/index.html deleted file mode 100644 index 7f36d6ce91..0000000000 --- a/_examples/auth/cors/public/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - Iris Cors Example - - - -
    -
- - - - - - \ No newline at end of file diff --git a/_examples/auth/cors/public/main.js b/_examples/auth/cors/public/main.js deleted file mode 100644 index aa83baf30d..0000000000 --- a/_examples/auth/cors/public/main.js +++ /dev/null @@ -1,43 +0,0 @@ -// https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch -async function doRequest(method = 'GET', url = '', data = {}) { - // Default options are marked with * - - const request = { - method: method, // *GET, POST, PUT, DELETE, etc. - mode: 'cors', // no-cors, *cors, same-origin - cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached - credentials: 'same-origin', // include, *same-origin, omit - redirect: 'follow', // manual, *follow, error - referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url - }; - - if (data !== undefined && method !== 'GET' && method !== 'HEAD') { - request.headers = { - 'Content-Type': 'application/json' - // 'Content-Type': 'application/x-www-form-urlencoded', - }; - // body data type must match "Content-Type" header. - request.body = JSON.stringify(data); - } - - const response = await fetch(url, request); - return response.json(); // parses JSON response into native JavaScript objects. -} - -const ul = document.getElementById("list"); - -function fetchData() { - console.log("sending request...") - - doRequest('GET', '/data').then(data => { - data.forEach(item => { - var li = document.createElement("li"); - li.appendChild(document.createTextNode(item.title)); - ul.appendChild(li); - }); - - console.log(data); // JSON data parsed by `response.json()` call. - }); -} - -document.getElementById("fetchBtn").onclick = fetchData; \ No newline at end of file diff --git a/_examples/auth/goth/main.go b/_examples/auth/goth/main.go deleted file mode 100644 index 0e79e972bb..0000000000 --- a/_examples/auth/goth/main.go +++ /dev/null @@ -1,407 +0,0 @@ -package main - -// Any OAuth2 (even the pure golang/x/net/oauth2) package -// can be used with iris but at this example we will see the markbates' goth: -// -// $ go get github.com/markbates/goth/... -// -// This OAuth2 example works with sessions, so we will need -// to attach a session manager. -// Optionally: for even more secure session values, -// developers can use any third-party package to add a custom cookie encoder/decoder. -// At this example we will use the gorilla's securecookie: -// -// $ go get github.com/gorilla/securecookie -// Example of securecookie can be found at "sessions/securecookie" example folder. - -// Notes: -// The whole example is converted by markbates/goth/example/main.go. -// It's tested with my own TWITTER application and it worked, even for localhost. -// I guess that everything else works as expected, all bugs reported by goth library's community -// are fixed in the time I wrote that example, have fun! -import ( - "errors" - "os" - "sort" - - "github.com/kataras/iris/v12" - - "github.com/kataras/iris/v12/sessions" - - "github.com/gorilla/securecookie" // optionally, used for session's encoder/decoder - - "github.com/markbates/goth" - "github.com/markbates/goth/providers/amazon" - "github.com/markbates/goth/providers/auth0" - "github.com/markbates/goth/providers/bitbucket" - "github.com/markbates/goth/providers/box" - "github.com/markbates/goth/providers/dailymotion" - "github.com/markbates/goth/providers/deezer" - "github.com/markbates/goth/providers/digitalocean" - "github.com/markbates/goth/providers/discord" - "github.com/markbates/goth/providers/dropbox" - "github.com/markbates/goth/providers/facebook" - "github.com/markbates/goth/providers/fitbit" - "github.com/markbates/goth/providers/github" - "github.com/markbates/goth/providers/gitlab" - "github.com/markbates/goth/providers/gplus" - "github.com/markbates/goth/providers/heroku" - "github.com/markbates/goth/providers/instagram" - "github.com/markbates/goth/providers/intercom" - "github.com/markbates/goth/providers/lastfm" - "github.com/markbates/goth/providers/linkedin" - "github.com/markbates/goth/providers/meetup" - "github.com/markbates/goth/providers/onedrive" - "github.com/markbates/goth/providers/openidConnect" - "github.com/markbates/goth/providers/paypal" - "github.com/markbates/goth/providers/salesforce" - "github.com/markbates/goth/providers/slack" - "github.com/markbates/goth/providers/soundcloud" - "github.com/markbates/goth/providers/spotify" - "github.com/markbates/goth/providers/steam" - "github.com/markbates/goth/providers/stripe" - "github.com/markbates/goth/providers/twitch" - "github.com/markbates/goth/providers/twitter" - "github.com/markbates/goth/providers/uber" - "github.com/markbates/goth/providers/wepay" - "github.com/markbates/goth/providers/xero" - "github.com/markbates/goth/providers/yahoo" - "github.com/markbates/goth/providers/yammer" -) - -var sessionsManager *sessions.Sessions - -func init() { - // attach a session manager - cookieName := "mycustomsessionid" - hashKey := securecookie.GenerateRandomKey(64) - blockKey := securecookie.GenerateRandomKey(32) - secureCookie := securecookie.New(hashKey, blockKey) - - sessionsManager = sessions.New(sessions.Config{ - Cookie: cookieName, - Encoding: secureCookie, - AllowReclaim: true, - }) -} - -// These are some function helpers that you may use if you want - -// GetProviderName is a function used to get the name of a provider -// for a given request. By default, this provider is fetched from -// the URL query string. If you provide it in a different way, -// assign your own function to this variable that returns the provider -// name for your request. -var GetProviderName = func(ctx iris.Context) (string, error) { - // try to get it from the url param "provider" - if p := ctx.URLParam("provider"); p != "" { - return p, nil - } - - // try to get it from the url PATH parameter "{provider} or :provider or {provider:string} or {provider:alphabetical}" - if p := ctx.Params().Get("provider"); p != "" { - return p, nil - } - - // try to get it from context's per-request storage - if p := ctx.Values().GetString("provider"); p != "" { - return p, nil - } - // if not found then return an empty string with the corresponding error - return "", errors.New("you must select a provider") -} - -/* -BeginAuthHandler is a convenience handler for starting the authentication process. -It expects to be able to get the name of the provider from the query parameters -as either "provider" or ":provider". - -BeginAuthHandler will redirect the user to the appropriate authentication end-point -for the requested provider. - -See https://github.com/markbates/goth/examples/main.go to see this in action. -*/ -func BeginAuthHandler(ctx iris.Context) { - url, err := GetAuthURL(ctx) - if err != nil { - ctx.StatusCode(iris.StatusBadRequest) - ctx.Writef("%v", err) - return - } - - ctx.Redirect(url, iris.StatusTemporaryRedirect) -} - -/* -GetAuthURL starts the authentication process with the requested provided. -It will return a URL that should be used to send users to. - -It expects to be able to get the name of the provider from the query parameters -as either "provider" or ":provider" or from the context's value of "provider" key. - -I would recommend using the BeginAuthHandler instead of doing all of these steps -yourself, but that's entirely up to you. -*/ -func GetAuthURL(ctx iris.Context) (string, error) { - providerName, err := GetProviderName(ctx) - if err != nil { - return "", err - } - - provider, err := goth.GetProvider(providerName) - if err != nil { - return "", err - } - sess, err := provider.BeginAuth(SetState(ctx)) - if err != nil { - return "", err - } - - url, err := sess.GetAuthURL() - if err != nil { - return "", err - } - session := sessionsManager.Start(ctx) - session.Set(providerName, sess.Marshal()) - return url, nil -} - -// SetState sets the state string associated with the given request. -// If no state string is associated with the request, one will be generated. -// This state is sent to the provider and can be retrieved during the -// callback. -var SetState = func(ctx iris.Context) string { - state := ctx.URLParam("state") - if len(state) > 0 { - return state - } - - return "state" -} - -// GetState gets the state returned by the provider during the callback. -// This is used to prevent CSRF attacks, see -// http://tools.ietf.org/html/rfc6749#section-10.12 -var GetState = func(ctx iris.Context) string { - return ctx.URLParam("state") -} - -/* -CompleteUserAuth does what it says on the tin. It completes the authentication -process and fetches all of the basic information about the user from the provider. - -It expects to be able to get the name of the provider from the query parameters -as either "provider" or ":provider". - -See https://github.com/markbates/goth/examples/main.go to see this in action. -*/ -var CompleteUserAuth = func(ctx iris.Context) (goth.User, error) { - providerName, err := GetProviderName(ctx) - if err != nil { - return goth.User{}, err - } - - provider, err := goth.GetProvider(providerName) - if err != nil { - return goth.User{}, err - } - session := sessionsManager.Start(ctx) - value := session.GetString(providerName) - if value == "" { - return goth.User{}, errors.New("session value for " + providerName + " not found") - } - - sess, err := provider.UnmarshalSession(value) - if err != nil { - return goth.User{}, err - } - - user, err := provider.FetchUser(sess) - if err == nil { - // user can be found with existing session data - return user, err - } - - // get new token and retry fetch - _, err = sess.Authorize(provider, ctx.Request().URL.Query()) - if err != nil { - return goth.User{}, err - } - - session.Set(providerName, sess.Marshal()) - return provider.FetchUser(sess) -} - -// Logout invalidates a user session. -func Logout(ctx iris.Context) error { - providerName, err := GetProviderName(ctx) - if err != nil { - return err - } - session := sessionsManager.Start(ctx) - session.Delete(providerName) - return nil -} - -// End of the "some function helpers". - -func main() { - goth.UseProviders( - twitter.New(os.Getenv("TWITTER_KEY"), os.Getenv("TWITTER_SECRET"), "http://localhost:3000/auth/twitter/callback"), - // If you'd like to use authenticate instead of authorize in Twitter provider, use this instead. - // twitter.NewAuthenticate(os.Getenv("TWITTER_KEY"), os.Getenv("TWITTER_SECRET"), "http://localhost:3000/auth/twitter/callback"), - - facebook.New(os.Getenv("FACEBOOK_KEY"), os.Getenv("FACEBOOK_SECRET"), "http://localhost:3000/auth/facebook/callback"), - fitbit.New(os.Getenv("FITBIT_KEY"), os.Getenv("FITBIT_SECRET"), "http://localhost:3000/auth/fitbit/callback"), - gplus.New(os.Getenv("GPLUS_KEY"), os.Getenv("GPLUS_SECRET"), "http://localhost:3000/auth/gplus/callback"), - github.New(os.Getenv("GITHUB_KEY"), os.Getenv("GITHUB_SECRET"), "http://localhost:3000/auth/github/callback"), - spotify.New(os.Getenv("SPOTIFY_KEY"), os.Getenv("SPOTIFY_SECRET"), "http://localhost:3000/auth/spotify/callback"), - linkedin.New(os.Getenv("LINKEDIN_KEY"), os.Getenv("LINKEDIN_SECRET"), "http://localhost:3000/auth/linkedin/callback"), - lastfm.New(os.Getenv("LASTFM_KEY"), os.Getenv("LASTFM_SECRET"), "http://localhost:3000/auth/lastfm/callback"), - twitch.New(os.Getenv("TWITCH_KEY"), os.Getenv("TWITCH_SECRET"), "http://localhost:3000/auth/twitch/callback"), - dropbox.New(os.Getenv("DROPBOX_KEY"), os.Getenv("DROPBOX_SECRET"), "http://localhost:3000/auth/dropbox/callback"), - digitalocean.New(os.Getenv("DIGITALOCEAN_KEY"), os.Getenv("DIGITALOCEAN_SECRET"), "http://localhost:3000/auth/digitalocean/callback", "read"), - bitbucket.New(os.Getenv("BITBUCKET_KEY"), os.Getenv("BITBUCKET_SECRET"), "http://localhost:3000/auth/bitbucket/callback"), - instagram.New(os.Getenv("INSTAGRAM_KEY"), os.Getenv("INSTAGRAM_SECRET"), "http://localhost:3000/auth/instagram/callback"), - intercom.New(os.Getenv("INTERCOM_KEY"), os.Getenv("INTERCOM_SECRET"), "http://localhost:3000/auth/intercom/callback"), - box.New(os.Getenv("BOX_KEY"), os.Getenv("BOX_SECRET"), "http://localhost:3000/auth/box/callback"), - salesforce.New(os.Getenv("SALESFORCE_KEY"), os.Getenv("SALESFORCE_SECRET"), "http://localhost:3000/auth/salesforce/callback"), - amazon.New(os.Getenv("AMAZON_KEY"), os.Getenv("AMAZON_SECRET"), "http://localhost:3000/auth/amazon/callback"), - yammer.New(os.Getenv("YAMMER_KEY"), os.Getenv("YAMMER_SECRET"), "http://localhost:3000/auth/yammer/callback"), - onedrive.New(os.Getenv("ONEDRIVE_KEY"), os.Getenv("ONEDRIVE_SECRET"), "http://localhost:3000/auth/onedrive/callback"), - - // Pointed localhost.com to http://localhost:3000/auth/yahoo/callback through proxy as yahoo - // does not allow to put custom ports in redirection uri - yahoo.New(os.Getenv("YAHOO_KEY"), os.Getenv("YAHOO_SECRET"), "http://localhost.com"), - slack.New(os.Getenv("SLACK_KEY"), os.Getenv("SLACK_SECRET"), "http://localhost:3000/auth/slack/callback"), - stripe.New(os.Getenv("STRIPE_KEY"), os.Getenv("STRIPE_SECRET"), "http://localhost:3000/auth/stripe/callback"), - wepay.New(os.Getenv("WEPAY_KEY"), os.Getenv("WEPAY_SECRET"), "http://localhost:3000/auth/wepay/callback", "view_user"), - // By default paypal production auth urls will be used, please set PAYPAL_ENV=sandbox as environment variable for testing - // in sandbox environment - paypal.New(os.Getenv("PAYPAL_KEY"), os.Getenv("PAYPAL_SECRET"), "http://localhost:3000/auth/paypal/callback"), - steam.New(os.Getenv("STEAM_KEY"), "http://localhost:3000/auth/steam/callback"), - heroku.New(os.Getenv("HEROKU_KEY"), os.Getenv("HEROKU_SECRET"), "http://localhost:3000/auth/heroku/callback"), - uber.New(os.Getenv("UBER_KEY"), os.Getenv("UBER_SECRET"), "http://localhost:3000/auth/uber/callback"), - soundcloud.New(os.Getenv("SOUNDCLOUD_KEY"), os.Getenv("SOUNDCLOUD_SECRET"), "http://localhost:3000/auth/soundcloud/callback"), - gitlab.New(os.Getenv("GITLAB_KEY"), os.Getenv("GITLAB_SECRET"), "http://localhost:3000/auth/gitlab/callback"), - dailymotion.New(os.Getenv("DAILYMOTION_KEY"), os.Getenv("DAILYMOTION_SECRET"), "http://localhost:3000/auth/dailymotion/callback", "email"), - deezer.New(os.Getenv("DEEZER_KEY"), os.Getenv("DEEZER_SECRET"), "http://localhost:3000/auth/deezer/callback", "email"), - discord.New(os.Getenv("DISCORD_KEY"), os.Getenv("DISCORD_SECRET"), "http://localhost:3000/auth/discord/callback", discord.ScopeIdentify, discord.ScopeEmail), - meetup.New(os.Getenv("MEETUP_KEY"), os.Getenv("MEETUP_SECRET"), "http://localhost:3000/auth/meetup/callback"), - - // Auth0 allocates domain per customer, a domain must be provided for auth0 to work - auth0.New(os.Getenv("AUTH0_KEY"), os.Getenv("AUTH0_SECRET"), "http://localhost:3000/auth/auth0/callback", os.Getenv("AUTH0_DOMAIN")), - xero.New(os.Getenv("XERO_KEY"), os.Getenv("XERO_SECRET"), "http://localhost:3000/auth/xero/callback"), - ) - - // OpenID Connect is based on OpenID Connect Auto Discovery URL (https://openid.net/specs/openid-connect-discovery-1_0-17.html) - // because the OpenID Connect provider initialize it self in the New(), it can return an error which should be handled or ignored - // ignore the error for now - openidConnect, _ := openidConnect.New(os.Getenv("OPENID_CONNECT_KEY"), os.Getenv("OPENID_CONNECT_SECRET"), "http://localhost:3000/auth/openid-connect/callback", os.Getenv("OPENID_CONNECT_DISCOVERY_URL")) - if openidConnect != nil { - goth.UseProviders(openidConnect) - } - - m := make(map[string]string) - m["amazon"] = "Amazon" - m["bitbucket"] = "Bitbucket" - m["box"] = "Box" - m["dailymotion"] = "Dailymotion" - m["deezer"] = "Deezer" - m["digitalocean"] = "Digital Ocean" - m["discord"] = "Discord" - m["dropbox"] = "Dropbox" - m["facebook"] = "Facebook" - m["fitbit"] = "Fitbit" - m["github"] = "Github" - m["gitlab"] = "Gitlab" - m["soundcloud"] = "SoundCloud" - m["spotify"] = "Spotify" - m["steam"] = "Steam" - m["stripe"] = "Stripe" - m["twitch"] = "Twitch" - m["uber"] = "Uber" - m["wepay"] = "Wepay" - m["yahoo"] = "Yahoo" - m["yammer"] = "Yammer" - m["gplus"] = "Google Plus" - m["heroku"] = "Heroku" - m["instagram"] = "Instagram" - m["intercom"] = "Intercom" - m["lastfm"] = "Last FM" - m["linkedin"] = "Linkedin" - m["onedrive"] = "Onedrive" - m["paypal"] = "Paypal" - m["twitter"] = "Twitter" - m["salesforce"] = "Salesforce" - m["slack"] = "Slack" - m["meetup"] = "Meetup.com" - m["auth0"] = "Auth0" - m["openid-connect"] = "OpenID Connect" - m["xero"] = "Xero" - - var keys []string - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - - providerIndex := &ProviderIndex{Providers: keys, ProvidersMap: m} - - // create our app, - // set a view - // set sessions - // and setup the router for the showcase - app := iris.New() - - // attach and build our templates - app.RegisterView(iris.HTML("./templates", ".html")) - - // start of the router - - app.Get("/auth/{provider}/callback", func(ctx iris.Context) { - user, err := CompleteUserAuth(ctx) - if err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.Writef("%v", err) - return - } - ctx.ViewData("", user) - if err := ctx.View("user.html"); err != nil { - ctx.Writef("%v", err) - } - }) - - app.Get("/logout/{provider}", func(ctx iris.Context) { - Logout(ctx) - ctx.Redirect("/", iris.StatusTemporaryRedirect) - }) - - app.Get("/auth/{provider}", func(ctx iris.Context) { - // try to get the user without re-authenticating - if gothUser, err := CompleteUserAuth(ctx); err == nil { - ctx.ViewData("", gothUser) - if err := ctx.View("user.html"); err != nil { - ctx.Writef("%v", err) - } - } else { - BeginAuthHandler(ctx) - } - }) - - app.Get("/", func(ctx iris.Context) { - ctx.ViewData("", providerIndex) - - if err := ctx.View("index.html"); err != nil { - ctx.Writef("%v", err) - } - }) - - // http://localhost:3000 - app.Listen("localhost:3000") -} - -type ProviderIndex struct { - Providers []string - ProvidersMap map[string]string -} diff --git a/_examples/auth/goth/templates/index.html b/_examples/auth/goth/templates/index.html deleted file mode 100644 index 53652804c6..0000000000 --- a/_examples/auth/goth/templates/index.html +++ /dev/null @@ -1,3 +0,0 @@ -{{range $key,$value:=.Providers}} -

Log in with {{index $.ProvidersMap $value}}

-{{end}} \ No newline at end of file diff --git a/_examples/auth/goth/templates/user.html b/_examples/auth/goth/templates/user.html deleted file mode 100644 index 10ab7c5cd8..0000000000 --- a/_examples/auth/goth/templates/user.html +++ /dev/null @@ -1,11 +0,0 @@ -

logout

-

Name: {{.Name}} [{{.LastName}}, {{.FirstName}}]

-

Email: {{.Email}}

-

NickName: {{.NickName}}

-

Location: {{.Location}}

-

AvatarURL: {{.AvatarURL}}

-

Description: {{.Description}}

-

UserID: {{.UserID}}

-

AccessToken: {{.AccessToken}}

-

ExpiresAt: {{.ExpiresAt}}

-

RefreshToken: {{.RefreshToken}}

\ No newline at end of file diff --git a/_examples/auth/hcaptcha/hosts b/_examples/auth/hcaptcha/hosts deleted file mode 100644 index d11c711b9a..0000000000 --- a/_examples/auth/hcaptcha/hosts +++ /dev/null @@ -1,3 +0,0 @@ -# https://docs.hcaptcha.com/#localdev -# Add to the end of your hosts file, e.g. on windows: C:/windows/system32/drivers/etc/hosts -127.0.0.1 yourdomain.com diff --git a/_examples/auth/hcaptcha/main.go b/_examples/auth/hcaptcha/main.go deleted file mode 100644 index 87e917acbc..0000000000 --- a/_examples/auth/hcaptcha/main.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "os" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/hcaptcha" -) - -// Get the following values from: https://dashboard.hcaptcha.com -// Also, check: https://docs.hcaptcha.com/#localdev to test on local environment. -var ( - siteKey = os.Getenv("HCAPTCHA-SITE-KEY") - secretKey = os.Getenv("HCAPTCHA-SECRET-KEY") -) - -func main() { - app := iris.New() - app.RegisterView(iris.HTML("./templates", ".html")) - - hCaptcha := hcaptcha.New(secretKey) - app.Get("/register", registerForm) - app.Post("/register", hCaptcha, register) // See `hcaptcha.SiteVerify` for manual validation too. - - app.Logger().Infof("SiteKey = %s\tSecretKey = %s", - siteKey, secretKey) - - // GET: http://yourdomain.com/register - app.Listen(":80") -} - -func register(ctx iris.Context) { - hcaptchaResp, ok := hcaptcha.Get(ctx) - if !ok { - ctx.StatusCode(iris.StatusUnauthorized) - ctx.WriteString("Are you a bot?") - return - } - - ctx.Writef("Register action here...action was asked by a Human.\nResponse value is: %#+v", hcaptchaResp) -} - -func registerForm(ctx iris.Context) { - ctx.ViewData("SiteKey", siteKey) - ctx.View("register_form.html") -} diff --git a/_examples/auth/hcaptcha/templates/register_form.html b/_examples/auth/hcaptcha/templates/register_form.html deleted file mode 100644 index 26a0cdc0be..0000000000 --- a/_examples/auth/hcaptcha/templates/register_form.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - hCaptcha Demo - - - - -
- - -
-
- -
- - - \ No newline at end of file diff --git a/_examples/auth/jwt/README.md b/_examples/auth/jwt/README.md deleted file mode 100644 index 1d2e0ae50a..0000000000 --- a/_examples/auth/jwt/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Generate RSA - -```sh -$ openssl genrsa -des3 -out private_rsa.pem 2048 -``` - -```go -b, err := ioutil.ReadFile("./private_rsa.pem") -if err != nil { - panic(err) -} -key := jwt.MustParseRSAPrivateKey(b, []byte("pass")) -``` - -OR - -```go -import "crypto/rand" -import "crypto/rsa" - -key, err := rsa.GenerateKey(rand.Reader, 2048) -``` - -# Generate Ed25519 - -```sh -$ openssl genpkey -algorithm Ed25519 -out private_ed25519.pem -$ openssl req -x509 -key private_ed25519.pem -out cert_ed25519.pem -days 365 -``` diff --git a/_examples/auth/jwt/main.go b/_examples/auth/jwt/main.go deleted file mode 100644 index 77f9648f1c..0000000000 --- a/_examples/auth/jwt/main.go +++ /dev/null @@ -1,155 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/jwt" -) - -// UserClaims a custom claims structure. You can just use jwt.Claims too. -type UserClaims struct { - jwt.Claims - Username string -} - -func main() { - // Get keys from system's environment variables - // JWT_SECRET (for signing and verification) and JWT_SECRET_ENC(for encryption and decryption), - // or defaults to "secret" and "itsa16bytesecret" respectfully. - // - // Use the `jwt.New` instead for more flexibility, if necessary. - j := jwt.HMAC(15*time.Minute, "secret", "itsa16bytesecret") - - app := iris.New() - app.Logger().SetLevel("debug") - - app.Get("/authenticate", func(ctx iris.Context) { - standardClaims := jwt.Claims{Issuer: "an-issuer", Audience: jwt.Audience{"an-audience"}} - // NOTE: if custom claims then the `j.Expiry(claims)` (or jwt.Expiry(duration, claims)) - // MUST be called in order to set the expiration time. - customClaims := UserClaims{ - Claims: j.Expiry(standardClaims), - Username: "kataras", - } - - j.WriteToken(ctx, customClaims) - }) - - userRouter := app.Party("/user") - { - // userRouter.Use(j.Verify) - // userRouter.Get("/", func(ctx iris.Context) { - // var claims UserClaims - // if err := jwt.ReadClaims(ctx, &claims); err != nil { - // // Validation-only errors, the rest are already - // // checked on `j.Verify` middleware. - // ctx.StopWithStatus(iris.StatusUnauthorized) - // return - // } - // - // ctx.Writef("Claims: %#+v\n", claims) - // }) - // - // OR: - userRouter.Get("/", func(ctx iris.Context) { - var claims UserClaims - if err := j.VerifyToken(ctx, &claims); err != nil { - ctx.StopWithStatus(iris.StatusUnauthorized) - return - } - - ctx.Writef("Claims: %#+v\n", claims) - }) - } - - app.Listen(":8080") -} - -/* -func default_RSA_Example() { - j := jwt.RSA(15*time.Minute) -} - -Same as: - -func load_File_Or_Generate_RSA_Example() { - signKey, err := jwt.LoadRSA("jwt_sign.key", 2048) - if err != nil { - panic(err) - } - - j, err := jwt.New(15*time.Minute, jwt.RS256, signKey) - if err != nil { - panic(err) - } - - encKey, err := jwt.LoadRSA("jwt_enc.key", 2048) - if err != nil { - panic(err) - } - err = j.WithEncryption(jwt.A128CBCHS256, jwt.RSA15, encKey) - if err != nil { - panic(err) - } -} -*/ - -/* -func hmac_Example() { - // hmac - key := []byte("secret") - j, err := jwt.New(15*time.Minute, jwt.HS256, key) - if err != nil { - panic(err) - } - - // OPTIONAL encryption: - encryptionKey := []byte("itsa16bytesecret") - err = j.WithEncryption(jwt.A128GCM, jwt.DIRECT, encryptionKey) - if err != nil { - panic(err) - } -} -*/ - -/* -func load_From_File_With_Password_Example() { - b, err := ioutil.ReadFile("./rsa_password_protected.key") - if err != nil { - panic(err) - } - signKey,err := jwt.ParseRSAPrivateKey(b, []byte("pass")) - if err != nil { - panic(err) - } - - j, err := jwt.New(15*time.Minute, jwt.RS256, signKey) - if err != nil { - panic(err) - } -} -*/ - -/* -func generate_RSA_Example() { - signKey, err := rsa.GenerateKey(rand.Reader, 4096) - if err != nil { - panic(err) - } - - encryptionKey, err := rsa.GenerateKey(rand.Reader, 4096) - if err != nil { - panic(err) - } - - j, err := jwt.New(15*time.Minute, jwt.RS512, signKey) - if err != nil { - panic(err) - } - err = j.WithEncryption(jwt.A128CBCHS256, jwt.RSA15, encryptionKey) - if err != nil { - panic(err) - } -} -*/ diff --git a/_examples/auth/jwt/rsa_password_protected.key b/_examples/auth/jwt/rsa_password_protected.key deleted file mode 100644 index e93fff7761..0000000000 --- a/_examples/auth/jwt/rsa_password_protected.key +++ /dev/null @@ -1,30 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,6B0BC214C94124FE - -lAM48DEM/GdCDimr9Vhi+fSHLgduDb0l2BA4uhILgNby51jxY/4X3IqM6f3ImKX7 -cEd9OBug+pwIugB0UW0L0f5Pd59Ovpiaz3xLci1/19ehYnMqsuP3YAnJm40hT5VP -p0gWRiR415PJ0fPeeJPFx5IsqvkTJ30LWZHUZX4EkdcL5L8PrVbmthGDbLh+OcMc -LzoP8eTglzlZF03nyvAol6+p2eZtvOJLu8nWG25q17kyBx6kEiCsWFcUBTX9G7sH -CM3naByDijqZXE/XXtmTMLSRRnlk7Q5WLxClroHlUP9y8BQFMo2TW4Z+vNjHUkc1 -77ghabX1704bAlIE8LLZJKrm/C5+VKyV6117SVG/2bc4036Y5rStXpANbk1j4K0x -ADvpRhuTpifaogdvJP+8eXBdl841MQMRzWuZHp6UNYYQegoV9C+KHyJx4UPjZyzd -gblZmKgU+BsX3mV6MLhJtd6dheLZtpBsAlSstJxzmgwqz9faONYEGeItXO+NnxbA -mxAp/mI+Fz2jfgYlWjwkyPPzD4k/ZMMzB4XLkKKs9XaxUtTomiDkuYZfnABhxt73 -xBy40V1rb/NyeW80pk1zEHM6Iy/48ETSp9U3k9sSOXjMhYbPXgxDtimV8w0qGFAo -2Tif7ZuaiuC38rOkoHK9C6vy2Dp8lQZ+QBnUKLeFsyhq9CaqSdnyUTMj3oEZXXf+ -TqqeO+PTtl7JaNfGRq6/aMZqxACHkyVUvYvjZzx07CJ2fr+OtNqxallM6Oc/o9NZ -5u7lpgrYaKM/b67q0d2X/AoxR5zrZuM8eam3acD1PwHFQKbJWuFNmjWtnlZNuR3X -fZEmxIKwDlup8TxFcqbbZtPHuQA2mTMTqfRkf8oPSO+N6NNaUpb0ignYyA7Eu5GT -b02d/oNLETMikxUxntMSH7GhuOpfJyELz8krYTttbJ+a93h4wBeYW2+LyAr/cRLB -mbtKLtaN7f3FaOSnu8e0+zlJ7xglHPXqblRL9q6ZDM5UJtJD4rA7LPZHk/0Y1Kb6 -hBh1qMDu0r3IV4X7MDacvxw7aa7D8TyXJiFSvxykVhds+ndjIe51Ics5908+lev3 -nwE69PLMwyqe2vvE2oDwao4XJuBLCHjcv/VagRSz/XQGMbZqb3L6unyd3UPl8JjP -ovipNwM4rFnE54uiUUeki7TZGDYO72vQcSaLrmbeAWc2m202+rqLz0WMm6HpPmCv -IgexpX2MnIeHJ3+BlEjA2u+S6xNSD7qHGk2pb7DD8nRvUdSHAHeaQbrkEfEhhR2Q -Dw5gdw1JyQ0UKBl5ndn/1Ub2Asl016lZjpqHyMIVS4tFixACDsihEYMmq/zQmTj4 -8oBZTU+fycN/KiGKZBsqxIwgYIeMz/GfvoyN5m57l6fwEZALVpveI1pP4fiZB/Z8 -xLKa5JK6L10lAD1YHWc1dPhamf9Sb3JwN2CFtGvjOJ/YjAZu3jJoxi40DtRkE3Rh -HI8Cbx1OORzoo0kO0vy42rz5qunYyVmEzPKtOj+YjVEhVJ85yJZ9bTZtuyqMv8mH -cnwEeIFK8cmm9asbVzQGDwN/UGB4cO3LrMX1RYk4GRttTGlp0729BbmZmu00RnD/ ------END RSA PRIVATE KEY----- diff --git a/_examples/auth/permissions/main.go b/_examples/auth/permissions/main.go deleted file mode 100644 index 026155b340..0000000000 --- a/_examples/auth/permissions/main.go +++ /dev/null @@ -1,118 +0,0 @@ -package main - -import ( - "fmt" - "log" - "strings" - - "github.com/kataras/iris/v12" - - permissions "github.com/xyproto/permissionbolt" - // * PostgreSQL support: - // permissions "github.com/xyproto/pstore" and - // perm, err := permissions.New(...) - // - // * MariaDB/MySQL support: - // permissions "github.com/xyproto/permissionsql" and - // perm, err := permissions.New/NewWithDSN(...) - // * Redis support: - // permissions "github.com/xyproto/permissions2" - // perm, err := permissions.New2() - // * Bolt support (this one): - // permissions "github.com/xyproto/permissionbolt" and - // perm, err := permissions.New(...) -) - -func main() { - app := iris.New() - app.Logger().SetLevel("debug") - - // New permissions middleware. - perm, err := permissions.New() - if err != nil { - log.Fatalln(err) - } - - // Blank slate, no default permissions - // perm.Clear() - - // Set up a middleware handler for Iris, with a custom "permission denied" message. - permissionHandler := func(ctx iris.Context) { - // Check if the user has the right admin/user rights - if perm.Rejected(ctx.ResponseWriter(), ctx.Request()) { - // Deny the request, don't call other middleware handlers - ctx.StopWithText(iris.StatusForbidden, "Permission denied!") - return - } - // Call the next middleware handler - ctx.Next() - } - - // Register the permissions middleware - app.Use(permissionHandler) - - // Get the userstate, used in the handlers below - userstate := perm.UserState() - - app.Get("/", func(ctx iris.Context) { - msg := "" - msg += fmt.Sprintf("Has user bob: %v\n", userstate.HasUser("bob")) - msg += fmt.Sprintf("Logged in on server: %v\n", userstate.IsLoggedIn("bob")) - msg += fmt.Sprintf("Is confirmed: %v\n", userstate.IsConfirmed("bob")) - msg += fmt.Sprintf("Username stored in cookies (or blank): %v\n", userstate.Username(ctx.Request())) - msg += fmt.Sprintf("Current user is logged in, has a valid cookie and *user rights*: %v\n", userstate.UserRights(ctx.Request())) - msg += fmt.Sprintf("Current user is logged in, has a valid cookie and *admin rights*: %v\n", userstate.AdminRights(ctx.Request())) - msg += fmt.Sprintln("\nTry: /register, /confirm, /remove, /login, /logout, /makeadmin, /clear, /data and /admin") - ctx.WriteString(msg) - }) - - app.Get("/register", func(ctx iris.Context) { - userstate.AddUser("bob", "hunter1", "bob@zombo.com") - ctx.Writef("User bob was created: %v\n", userstate.HasUser("bob")) - }) - - app.Get("/confirm", func(ctx iris.Context) { - userstate.MarkConfirmed("bob") - ctx.Writef("User bob was confirmed: %v\n", userstate.IsConfirmed("bob")) - }) - - app.Get("/remove", func(ctx iris.Context) { - userstate.RemoveUser("bob") - ctx.Writef("User bob was removed: %v\n", !userstate.HasUser("bob")) - }) - - app.Get("/login", func(ctx iris.Context) { - // Headers will be written, for storing a cookie - userstate.Login(ctx.ResponseWriter(), "bob") - ctx.Writef("bob is now logged in: %v\n", userstate.IsLoggedIn("bob")) - }) - - app.Get("/logout", func(ctx iris.Context) { - userstate.Logout("bob") - ctx.Writef("bob is now logged out: %v\n", !userstate.IsLoggedIn("bob")) - }) - - app.Get("/makeadmin", func(ctx iris.Context) { - userstate.SetAdminStatus("bob") - ctx.Writef("bob is now administrator: %v\n", userstate.IsAdmin("bob")) - }) - - app.Get("/clear", func(ctx iris.Context) { - userstate.ClearCookie(ctx.ResponseWriter()) - ctx.WriteString("Clearing cookie") - }) - - app.Get("/data", func(ctx iris.Context) { - ctx.WriteString("user page that only logged in users must see!") - }) - - app.Get("/admin", func(ctx iris.Context) { - ctx.WriteString("super secret information that only logged in administrators must see!\n\n") - if usernames, err := userstate.AllUsernames(); err == nil { - ctx.Writef("list of all users: %s" + strings.Join(usernames, ", ")) - } - }) - - // Serve - app.Listen(":8080") -} diff --git a/_examples/auth/recaptcha/custom_form/main.go b/_examples/auth/recaptcha/custom_form/main.go deleted file mode 100644 index 395c7c45a1..0000000000 --- a/_examples/auth/recaptcha/custom_form/main.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/kataras/iris/v12" - - "github.com/kataras/iris/v12/middleware/recaptcha" -) - -// keys should be obtained by https://www.google.com/recaptcha -const ( - recaptchaPublic = "6Lf3WywUAAAAAKNfAm5DP2J5ahqedtZdHTYaKkJ6" - recaptchaSecret = "6Lf3WywUAAAAAJpArb8nW_LCL_PuPuokmEABFfgw" -) - -func main() { - app := iris.New() - - r := recaptcha.New(recaptchaSecret) - - app.Get("/comment", showRecaptchaForm) - - // pass the middleware before the main handler or use the `recaptcha.SiteVerify`. - app.Post("/comment", r, postComment) - - app.Listen(":8080") -} - -var htmlForm = `
- -
- -
` - -func showRecaptchaForm(ctx iris.Context) { - contents := fmt.Sprintf(htmlForm, recaptchaPublic) - ctx.HTML(contents) -} - -func postComment(ctx iris.Context) { - // [...] - ctx.JSON(iris.Map{"success": true}) -} diff --git a/_examples/auth/recaptcha/main.go b/_examples/auth/recaptcha/main.go deleted file mode 100644 index 0648bede7a..0000000000 --- a/_examples/auth/recaptcha/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/recaptcha" -) - -// keys should be obtained by https://www.google.com/recaptcha -const ( - recaptchaPublic = "" - recaptchaSecret = "" -) - -func showRecaptchaForm(ctx iris.Context, path string) { - ctx.HTML(recaptcha.GetFormHTML(recaptchaPublic, path)) -} - -func main() { - app := iris.New() - - // On both Get and Post on this example, so you can easly - // use a single route to show a form and the main subject if recaptcha's validation result succeed. - app.HandleMany("GET POST", "/", func(ctx iris.Context) { - if ctx.Method() == iris.MethodGet { - showRecaptchaForm(ctx, "/") - return - } - - result := recaptcha.SiteVerify(ctx, recaptchaSecret) - if !result.Success { - /* redirect here if u want or do nothing */ - ctx.HTML(" failed please try again ") - return - } - - ctx.Writef("succeed.") - }) - - app.Listen(":8080") -} diff --git a/_examples/bootstrapper/bootstrap/bootstrapper.go b/_examples/bootstrapper/bootstrap/bootstrapper.go deleted file mode 100644 index 0d24c50cc1..0000000000 --- a/_examples/bootstrapper/bootstrap/bootstrapper.go +++ /dev/null @@ -1,123 +0,0 @@ -package bootstrap - -import ( - "time" - - "github.com/gorilla/securecookie" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/logger" - "github.com/kataras/iris/v12/middleware/recover" - "github.com/kataras/iris/v12/sessions" - "github.com/kataras/iris/v12/websocket" -) - -type Configurator func(*Bootstrapper) - -type Bootstrapper struct { - *iris.Application - AppName string - AppOwner string - AppSpawnDate time.Time - - Sessions *sessions.Sessions -} - -// New returns a new Bootstrapper. -func New(appName, appOwner string, cfgs ...Configurator) *Bootstrapper { - b := &Bootstrapper{ - AppName: appName, - AppOwner: appOwner, - AppSpawnDate: time.Now(), - Application: iris.New(), - } - - for _, cfg := range cfgs { - cfg(b) - } - - return b -} - -// SetupViews loads the templates. -func (b *Bootstrapper) SetupViews(viewsDir string) { - b.RegisterView(iris.HTML(viewsDir, ".html").Layout("shared/layout.html")) -} - -// SetupSessions initializes the sessions, optionally. -func (b *Bootstrapper) SetupSessions(expires time.Duration, cookieHashKey, cookieBlockKey []byte) { - b.Sessions = sessions.New(sessions.Config{ - Cookie: "SECRET_SESS_COOKIE_" + b.AppName, - Expires: expires, - Encoding: securecookie.New(cookieHashKey, cookieBlockKey), - }) -} - -// SetupWebsockets prepares the websocket server. -func (b *Bootstrapper) SetupWebsockets(endpoint string, handler websocket.ConnHandler) { - ws := websocket.New(websocket.DefaultGorillaUpgrader, handler) - - b.Get(endpoint, websocket.Handler(ws)) -} - -// SetupErrorHandlers prepares the http error handlers -// `(context.StatusCodeNotSuccessful`, which defaults to >=400 (but you can change it). -func (b *Bootstrapper) SetupErrorHandlers() { - b.OnAnyErrorCode(func(ctx iris.Context) { - err := iris.Map{ - "app": b.AppName, - "status": ctx.GetStatusCode(), - "message": ctx.Values().GetString("message"), - } - - if jsonOutput := ctx.URLParamExists("json"); jsonOutput { - ctx.JSON(err) - return - } - - ctx.ViewData("Err", err) - ctx.ViewData("Title", "Error") - ctx.View("shared/error.html") - }) -} - -const ( - // StaticAssets is the root directory for public assets like images, css, js. - StaticAssets = "./public/" - // Favicon is the relative 9to the "StaticAssets") favicon path for our app. - Favicon = "favicon.ico" -) - -// Configure accepts configurations and runs them inside the Bootstraper's context. -func (b *Bootstrapper) Configure(cs ...Configurator) { - for _, c := range cs { - c(b) - } -} - -// Bootstrap prepares our application. -// -// Returns itself. -func (b *Bootstrapper) Bootstrap() *Bootstrapper { - b.SetupViews("./views") - b.SetupSessions(24*time.Hour, - []byte("the-big-and-secret-fash-key-here"), - []byte("lot-secret-of-characters-big-too"), - ) - b.SetupErrorHandlers() - - // static files - b.Favicon(StaticAssets + Favicon) - b.HandleDir("/public", iris.Dir(StaticAssets)) - - // middleware, after static files - b.Use(recover.New()) - b.Use(logger.New()) - - return b -} - -// Listen starts the http server with the specified "addr". -func (b *Bootstrapper) Listen(addr string, cfgs ...iris.Configurator) { - b.Run(iris.Addr(addr), cfgs...) -} diff --git a/_examples/bootstrapper/folder_structure.png b/_examples/bootstrapper/folder_structure.png deleted file mode 100644 index 8dfba29fde..0000000000 Binary files a/_examples/bootstrapper/folder_structure.png and /dev/null differ diff --git a/_examples/bootstrapper/main.go b/_examples/bootstrapper/main.go deleted file mode 100644 index 4a8e1d1e2b..0000000000 --- a/_examples/bootstrapper/main.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12/_examples/bootstrapper/bootstrap" - "github.com/kataras/iris/v12/_examples/bootstrapper/middleware/identity" - "github.com/kataras/iris/v12/_examples/bootstrapper/routes" -) - -func newApp() *bootstrap.Bootstrapper { - app := bootstrap.New("Awesome App", "kataras2006@hotmail.com") - app.Bootstrap() - app.Configure(identity.Configure, routes.Configure) - return app -} - -func main() { - app := newApp() - app.Listen(":8080") -} diff --git a/_examples/bootstrapper/main_test.go b/_examples/bootstrapper/main_test.go deleted file mode 100644 index 959c04eb2f..0000000000 --- a/_examples/bootstrapper/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -// go test -v -func TestApp(t *testing.T) { - app := newApp() - e := httptest.New(t, app.Application) - - // test our routes - e.GET("/").Expect().Status(httptest.StatusOK) - e.GET("/follower/42").Expect().Status(httptest.StatusOK). - Body().Equal("from /follower/{id:int64} with ID: 42") - e.GET("/following/52").Expect().Status(httptest.StatusOK). - Body().Equal("from /following/{id:int64} with ID: 52") - e.GET("/like/64").Expect().Status(httptest.StatusOK). - Body().Equal("from /like/{id:int64} with ID: 64") - - // test not found - e.GET("/notfound").Expect().Status(httptest.StatusNotFound) - expectedErr := map[string]interface{}{ - "app": app.AppName, - "status": httptest.StatusNotFound, - "message": "", - } - e.GET("/anotfoundwithjson").WithQuery("json", nil). - Expect().Status(httptest.StatusNotFound).JSON().Equal(expectedErr) -} diff --git a/_examples/bootstrapper/middleware/identity/identity.go b/_examples/bootstrapper/middleware/identity/identity.go deleted file mode 100644 index 6ce7f3098a..0000000000 --- a/_examples/bootstrapper/middleware/identity/identity.go +++ /dev/null @@ -1,33 +0,0 @@ -package identity - -import ( - "time" - - "github.com/kataras/iris/v12" - - "github.com/kataras/iris/v12/_examples/bootstrapper/bootstrap" -) - -// New returns a new handler which adds some headers and view data -// describing the application, i.e the owner, the startup time. -func New(b *bootstrap.Bootstrapper) iris.Handler { - return func(ctx iris.Context) { - // response headers - ctx.Header("App-Name", b.AppName) - ctx.Header("App-Owner", b.AppOwner) - ctx.Header("App-Since", time.Since(b.AppSpawnDate).String()) - - ctx.Header("Server", "Iris: https://iris-go.com") - - // view data if ctx.View or c.Tmpl = "$page.html" will be called next. - ctx.ViewData("AppName", b.AppName) - ctx.ViewData("AppOwner", b.AppOwner) - ctx.Next() - } -} - -// Configure creates a new identity middleware and registers that to the app. -func Configure(b *bootstrap.Bootstrapper) { - h := New(b) - b.UseGlobal(h) -} diff --git a/_examples/bootstrapper/public/favicon.ico b/_examples/bootstrapper/public/favicon.ico deleted file mode 100644 index c370da518e..0000000000 Binary files a/_examples/bootstrapper/public/favicon.ico and /dev/null differ diff --git a/_examples/bootstrapper/routes/follower.go b/_examples/bootstrapper/routes/follower.go deleted file mode 100644 index 7c3113f60d..0000000000 --- a/_examples/bootstrapper/routes/follower.go +++ /dev/null @@ -1,11 +0,0 @@ -package routes - -import ( - "github.com/kataras/iris/v12" -) - -// GetFollowerHandler handles the GET: /follower/{id} -func GetFollowerHandler(ctx iris.Context) { - id, _ := ctx.Params().GetInt64("id") - ctx.Writef("from "+ctx.GetCurrentRoute().Path()+" with ID: %d", id) -} diff --git a/_examples/bootstrapper/routes/following.go b/_examples/bootstrapper/routes/following.go deleted file mode 100644 index affbbaf768..0000000000 --- a/_examples/bootstrapper/routes/following.go +++ /dev/null @@ -1,11 +0,0 @@ -package routes - -import ( - "github.com/kataras/iris/v12" -) - -// GetFollowingHandler handles the GET: /following/{id} -func GetFollowingHandler(ctx iris.Context) { - id, _ := ctx.Params().GetInt64("id") - ctx.Writef("from "+ctx.GetCurrentRoute().Path()+" with ID: %d", id) -} diff --git a/_examples/bootstrapper/routes/index.go b/_examples/bootstrapper/routes/index.go deleted file mode 100644 index 4ce214d483..0000000000 --- a/_examples/bootstrapper/routes/index.go +++ /dev/null @@ -1,11 +0,0 @@ -package routes - -import ( - "github.com/kataras/iris/v12" -) - -// GetIndexHandler handles the GET: / -func GetIndexHandler(ctx iris.Context) { - ctx.ViewData("Title", "Index Page") - ctx.View("index.html") -} diff --git a/_examples/bootstrapper/routes/like.go b/_examples/bootstrapper/routes/like.go deleted file mode 100644 index 3e15093835..0000000000 --- a/_examples/bootstrapper/routes/like.go +++ /dev/null @@ -1,11 +0,0 @@ -package routes - -import ( - "github.com/kataras/iris/v12" -) - -// GetLikeHandler handles the GET: /like/{id} -func GetLikeHandler(ctx iris.Context) { - id, _ := ctx.Params().GetInt64("id") - ctx.Writef("from "+ctx.GetCurrentRoute().Path()+" with ID: %d", id) -} diff --git a/_examples/bootstrapper/routes/routes.go b/_examples/bootstrapper/routes/routes.go deleted file mode 100644 index 3f52ea75cc..0000000000 --- a/_examples/bootstrapper/routes/routes.go +++ /dev/null @@ -1,13 +0,0 @@ -package routes - -import ( - "github.com/kataras/iris/v12/_examples/bootstrapper/bootstrap" -) - -// Configure registers the necessary routes to the app. -func Configure(b *bootstrap.Bootstrapper) { - b.Get("/", GetIndexHandler) - b.Get("/follower/{id:int64}", GetFollowerHandler) - b.Get("/following/{id:int64}", GetFollowingHandler) - b.Get("/like/{id:int64}", GetLikeHandler) -} diff --git a/_examples/bootstrapper/views/index.html b/_examples/bootstrapper/views/index.html deleted file mode 100644 index 4a813af605..0000000000 --- a/_examples/bootstrapper/views/index.html +++ /dev/null @@ -1 +0,0 @@ -

Welcome!!

\ No newline at end of file diff --git a/_examples/bootstrapper/views/shared/error.html b/_examples/bootstrapper/views/shared/error.html deleted file mode 100644 index 9fde08ad60..0000000000 --- a/_examples/bootstrapper/views/shared/error.html +++ /dev/null @@ -1,5 +0,0 @@ -

Error.

-

An error occurred while processing your request.

- -

{{.Err.status}}

-

{{.Err.message}}

\ No newline at end of file diff --git a/_examples/bootstrapper/views/shared/layout.html b/_examples/bootstrapper/views/shared/layout.html deleted file mode 100644 index cf92044434..0000000000 --- a/_examples/bootstrapper/views/shared/layout.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - {{.Title}} - {{.AppName}} - - - - -
- - {{ yield }} -
-
-

© 2017 - {{.AppOwner}}

-
-
- - - \ No newline at end of file diff --git a/_examples/caddy/Caddyfile b/_examples/caddy/Caddyfile deleted file mode 100644 index ecbee979aa..0000000000 --- a/_examples/caddy/Caddyfile +++ /dev/null @@ -1,9 +0,0 @@ -example.com { - header / Server "Iris" - proxy / example.com:9091 # localhost:9091 -} - -api.example.com { - header / Server "Iris" - proxy / api.example.com:9092 # localhost:9092 -} \ No newline at end of file diff --git a/_examples/caddy/README.md b/_examples/caddy/README.md deleted file mode 100644 index e8c601c35b..0000000000 --- a/_examples/caddy/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Caddy loves Iris - -The `Caddyfile` shows how you can use caddy to listen on ports 80 & 443 and sit in front of iris webserver(s) that serving on a different port (9091 and 9092 in this case; see Caddyfile). - -## Running our two web servers - -1. Go to `$GOPATH/src/github.com/kataras/iris/_examples/caddy/server1` -2. Open a terminal window and execute `go run main.go` -3. Go to `$GOPATH/src/github.com/kataras/iris/_examples/caddy/server2` -4. Open a new terminal window and execute `go run main.go` - -## Caddy installation - -1. Download caddy: https://caddyserver.com/download -2. Extract its contents where the `Caddyfile` is located, the `$GOPATH/src/github.com/kataras/iris/_examples/caddy` in this case -3. Open, read and modify the `Caddyfile` to see by yourself how easy it is to configure the servers -4. Run `caddy` directly or open a terminal window and execute `caddy` -5. Go to `https://example.com` and `https://api.example.com/user/42` - - -## Notes - -Iris has the `app.Run(iris.AutoTLS(":443", "example.com", "mail@example.com"))` which does -the exactly same thing but caddy is a great tool that helps you when you run multiple web servers from one host machine, i.e iris, apache, tomcat. \ No newline at end of file diff --git a/_examples/caddy/server1/main.go b/_examples/caddy/server1/main.go deleted file mode 100644 index 0f7f87ec9f..0000000000 --- a/_examples/caddy/server1/main.go +++ /dev/null @@ -1,49 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" -) - -func main() { - app := iris.New() - - templates := iris.HTML("./views", ".html").Layout("shared/layout.html") - app.RegisterView(templates) - - mvc.New(app).Handle(new(Controller)) - - // http://localhost:9091 - app.Listen(":9091") -} - -// Layout contains all the binding properties for the shared/layout.html -type Layout struct { - Title string -} - -// Controller is our example controller, request-scoped, each request has its own instance. -type Controller struct { - Layout Layout -} - -// BeginRequest is the first method fired when client requests from this Controller's root path. -func (c *Controller) BeginRequest(ctx iris.Context) { - c.Layout.Title = "Home Page" -} - -// EndRequest is the last method fired. -// It's here just to complete the BaseController -// in order to be tell iris to call the `BeginRequest` before the main method. -func (c *Controller) EndRequest(ctx iris.Context) {} - -// Get handles GET http://localhost:9091 -func (c *Controller) Get() mvc.View { - return mvc.View{ - Name: "index.html", - Data: iris.Map{ - "Layout": c.Layout, - "Message": "Welcome to my website!", - }, - } -} diff --git a/_examples/caddy/server1/views/index.html b/_examples/caddy/server1/views/index.html deleted file mode 100644 index 3d4a81f613..0000000000 --- a/_examples/caddy/server1/views/index.html +++ /dev/null @@ -1,3 +0,0 @@ -
- {{.Message}} -
\ No newline at end of file diff --git a/_examples/caddy/server1/views/shared/layout.html b/_examples/caddy/server1/views/shared/layout.html deleted file mode 100644 index 141a75f75c..0000000000 --- a/_examples/caddy/server1/views/shared/layout.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - {{.Layout.Title}} - - - - {{ yield }} - - - \ No newline at end of file diff --git a/_examples/caddy/server2/main.go b/_examples/caddy/server2/main.go deleted file mode 100644 index 45a9ed80cb..0000000000 --- a/_examples/caddy/server2/main.go +++ /dev/null @@ -1,68 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" -) - -type postValue func(string) string - -func main() { - app := iris.New() - - mvc.New(app.Party("/user")).Register( - func(ctx iris.Context) postValue { - return ctx.PostValue - }).Handle(new(UserController)) - - // GET http://localhost:9092/user - // GET http://localhost:9092/user/42 - // POST http://localhost:9092/user - // PUT http://localhost:9092/user/42 - // DELETE http://localhost:9092/user/42 - // GET http://localhost:9092/user/followers/42 - app.Listen(":9092") -} - -// UserController is our user example controller. -type UserController struct{} - -// Get handles GET /user -func (c *UserController) Get() string { - return "Select all users" -} - -// User is our test User model, nothing tremendous here. -type User struct{ ID int64 } - -// GetBy handles GET /user/42, equal to .Get("/user/{id:int64}") -func (c *UserController) GetBy(id int64) User { - // Select User by ID == $id. - return User{id} -} - -// Post handles POST /user -func (c *UserController) Post(post postValue) string { - username := post("username") - return "Create by user with username: " + username -} - -// PutBy handles PUT /user/42 -func (c *UserController) PutBy(id int) string { - // Update user by ID == $id - return "User updated" -} - -// DeleteBy handles DELETE /user/42 -func (c *UserController) DeleteBy(id int) bool { - // Delete user by ID == %id - // - // when boolean then true = iris.StatusOK, false = iris.StatusNotFound - return true -} - -// GetFollowersBy handles GET /user/followers/42 -func (c *UserController) GetFollowersBy(id int) []User { - // Select all followers by user ID == $id - return []User{ /* ... */ } -} diff --git a/_examples/compression/client-using-iris/main.go b/_examples/compression/client-using-iris/main.go deleted file mode 100644 index 65d9713152..0000000000 --- a/_examples/compression/client-using-iris/main.go +++ /dev/null @@ -1,112 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - - "github.com/kataras/iris/v12/context" -) - -const baseURL = "http://localhost:8080" - -// Available options: -// - "gzip", -// - "deflate", -// - "br" (for brotli), -// - "snappy" and -// - "s2" -const encoding = context.BROTLI - -var client = http.DefaultClient - -func main() { - fmt.Printf("Running client example on: %s\n", baseURL) - - getExample() - postExample() -} - -func getExample() { - endpoint := baseURL + "/" - req, err := http.NewRequest(http.MethodGet, endpoint, nil) - if err != nil { - panic(err) - } - // Required to receive server's compressed data. - req.Header.Set("Accept-Encoding", encoding) - - resp, err := client.Do(req) - if err != nil { - panic(err) - } - defer resp.Body.Close() - - // decompress server's compressed reply. - cr, err := context.NewCompressReader(resp.Body, encoding) - if err != nil { - panic(err) - } - defer cr.Close() - - body, err := ioutil.ReadAll(cr) - if err != nil { - panic(err) - } - - fmt.Printf("Received from server: %s", string(body)) -} - -type payload struct { - Username string `json:"username"` -} - -func postExample() { - buf := new(bytes.Buffer) - - // Compress client's data. - cw, err := context.NewCompressWriter(buf, encoding, -1) - if err != nil { - panic(err) - } - - json.NewEncoder(cw).Encode(payload{Username: "Edward"}) - - // `Close` or `Flush` required before `NewRequest` call. - cw.Close() - - endpoint := baseURL + "/" - - req, err := http.NewRequest(http.MethodPost, endpoint, buf) - if err != nil { - panic(err) - } - req.Header.Set("Content-Type", "application/json") - - // Required to send gzip compressed data to the server. - req.Header.Set("Content-Encoding", encoding) - // Required to receive server's compressed data. - req.Header.Set("Accept-Encoding", encoding) - - resp, err := client.Do(req) - if err != nil { - panic(err) - } - defer resp.Body.Close() - - // Decompress server's compressed reply. - cr, err := context.NewCompressReader(resp.Body, encoding) - if err != nil { - panic(err) - } - defer cr.Close() - - body, err := ioutil.ReadAll(cr) - if err != nil { - panic(err) - } - - fmt.Printf("Server replied with: %s", string(body)) -} diff --git a/_examples/compression/client/main.go b/_examples/compression/client/main.go deleted file mode 100644 index 6c39294a8e..0000000000 --- a/_examples/compression/client/main.go +++ /dev/null @@ -1,102 +0,0 @@ -package main - -import ( - "bytes" - "compress/gzip" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" -) - -var client = http.DefaultClient - -const baseURL = "http://localhost:8080" - -func main() { - fmt.Printf("Running client example on: %s\n", baseURL) - - getExample() - postExample() -} - -func getExample() { - endpoint := baseURL + "/" - req, err := http.NewRequest(http.MethodGet, endpoint, nil) - if err != nil { - panic(err) - } - // Required to receive server's compressed data. - req.Header.Set("Accept-Encoding", "gzip") - - resp, err := client.Do(req) - if err != nil { - panic(err) - } - defer resp.Body.Close() - - // decompress server's compressed reply. - r, err := gzip.NewReader(resp.Body) - if err != nil { - panic(err) - } - defer r.Close() - - body, err := ioutil.ReadAll(r) - if err != nil { - panic(err) - } - - fmt.Printf("Received from server: %s", string(body)) -} - -type payload struct { - Username string `json:"username"` -} - -func postExample() { - buf := new(bytes.Buffer) - - // Compress client's data. - w := gzip.NewWriter(buf) - - b, err := json.Marshal(payload{Username: "Edward"}) - if err != nil { - panic(err) - } - w.Write(b) - w.Close() - - endpoint := baseURL + "/" - - req, err := http.NewRequest(http.MethodPost, endpoint, buf) - if err != nil { - panic(err) - } - req.Header.Set("Content-Type", "application/json") - - // Required to send gzip compressed data to the server. - req.Header.Set("Content-Encoding", "gzip") - // Required to receive server's compressed data. - req.Header.Set("Accept-Encoding", "gzip") - - resp, err := client.Do(req) - if err != nil { - panic(err) - } - defer resp.Body.Close() - - // Decompress server's compressed reply. - r, err := gzip.NewReader(resp.Body) - if err != nil { - panic(err) - } - defer r.Close() - - body, err := ioutil.ReadAll(r) - if err != nil { - panic(err) - } - - fmt.Printf("Server replied with: %s", string(body)) -} diff --git a/_examples/compression/main.go b/_examples/compression/main.go deleted file mode 100644 index e1d989d710..0000000000 --- a/_examples/compression/main.go +++ /dev/null @@ -1,64 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := newApp() - app.Logger().SetLevel("debug") - app.Listen(":8080") -} - -func newApp() *iris.Application { - app := iris.New() - // HERE and you are ready to GO: - app.Use(iris.Compression) - - app.Get("/", send) - app.Post("/", receive) - - return app -} - -type payload struct { - Username string `json:"username"` -} - -func send(ctx iris.Context) { - ctx.JSON(payload{ - Username: "Makis", - }) -} - -func receive(ctx iris.Context) { - var p payload - if err := ctx.ReadJSON(&p); err != nil { - ctx.Application().Logger().Debugf("ReadJSON: %v", err) - } - - ctx.WriteString(p.Username) -} - -/* Manually: -func enableCompression(ctx iris.Context) { - // Enable writing using compression (deflate, gzip, brotli, snappy, s2): - err := ctx.CompressWriter(true) - if err != nil { - ctx.Application().Logger().Debugf("writer: %v", err) - // if you REQUIRE server to SEND compressed data then `return` here. - // return - } - - // Enable reading and binding request's compressed data: - err = ctx.CompressReader(true) - if err != nil && - // on GET we don't expect writing with gzip from client - ctx.Method() != iris.MethodGet { - ctx.Application().Logger().Debugf("reader: %v", err) - // if you REQUIRE server to RECEIVE only - // compressed data then `return` here. - // return - } - - ctx.Next() -} -*/ diff --git a/_examples/compression/main_test.go b/_examples/compression/main_test.go deleted file mode 100644 index 62cbb45913..0000000000 --- a/_examples/compression/main_test.go +++ /dev/null @@ -1,84 +0,0 @@ -package main - -import ( - "encoding/json" - "reflect" - "strings" - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/httptest" -) - -func TestCompression(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - var expectedReply = payload{Username: "Makis"} - testBody(t, e.GET("/"), expectedReply) -} - -func TestCompressionAfterRecorder(t *testing.T) { - var expectedReply = payload{Username: "Makis"} - - app := iris.New() - app.Use(func(ctx iris.Context) { - ctx.Record() - ctx.Next() - }) - app.Use(iris.Compression) - - app.Get("/", func(ctx iris.Context) { - ctx.JSON(expectedReply) - }) - - e := httptest.New(t, app) - testBody(t, e.GET("/"), expectedReply) -} - -func TestCompressionBeforeRecorder(t *testing.T) { - var expectedReply = payload{Username: "Makis"} - - app := iris.New() - app.Use(iris.Compression) - app.Use(func(ctx iris.Context) { - ctx.Record() - ctx.Next() - }) - - app.Get("/", func(ctx iris.Context) { - ctx.JSON(expectedReply) - }) - - e := httptest.New(t, app) - testBody(t, e.GET("/"), expectedReply) -} - -func testBody(t *testing.T, req *httptest.Request, expectedReply interface{}) { - t.Helper() - - body := req.WithHeader(context.AcceptEncodingHeaderKey, context.GZIP).Expect(). - Status(httptest.StatusOK). - ContentEncoding(context.GZIP). - ContentType(context.ContentJSONHeaderValue).Body().Raw() - - // Note that .Expect() consumes the response body - // and stores it to unexported "contents" field - // therefore, we retrieve it as string and put it to a new buffer. - r := strings.NewReader(body) - cr, err := context.NewCompressReader(r, context.GZIP) - if err != nil { - t.Fatal(err) - } - defer cr.Close() - - var got payload - if err = json.NewDecoder(cr).Decode(&got); err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(expectedReply, got) { - t.Fatalf("expected %#+v but got %#+v", expectedReply, got) - } -} diff --git a/_examples/configuration/from-configuration-structure/main.go b/_examples/configuration/from-configuration-structure/main.go deleted file mode 100644 index 93908cb4e2..0000000000 --- a/_examples/configuration/from-configuration-structure/main.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - app.Get("/", func(ctx iris.Context) { - ctx.HTML("Hello!") - }) - // [...] - - // Good when you want to modify the whole configuration. - app.Listen(":8080", iris.WithConfiguration(iris.Configuration{ // default configuration: - DisableStartupLog: false, - DisableInterruptHandler: false, - DisablePathCorrection: false, - EnablePathEscape: false, - FireMethodNotAllowed: false, - DisableBodyConsumptionOnUnmarshal: false, - DisableAutoFireStatusCode: false, - TimeFormat: "Mon, 02 Jan 2006 15:04:05 GMT", - Charset: "utf-8", - })) - - // or before Run: - // app.Configure(iris.WithConfiguration(iris.Configuration{...})) -} diff --git a/_examples/configuration/from-toml-file/configs/iris.tml b/_examples/configuration/from-toml-file/configs/iris.tml deleted file mode 100644 index 6eee1e11cb..0000000000 --- a/_examples/configuration/from-toml-file/configs/iris.tml +++ /dev/null @@ -1,9 +0,0 @@ -DisablePathCorrection = false -EnablePathEscape = false -FireMethodNotAllowed = true -DisableBodyConsumptionOnUnmarshal = false -TimeFormat = "Mon, 01 Jan 2006 15:04:05 GMT" -Charset = "utf-8" -RemoteAddrHeaders = ["X-Real-Ip", "X-Forwarded-For", "CF-Connecting-IP"] -[Other] - MyServerName = "iris" diff --git a/_examples/configuration/from-toml-file/main.go b/_examples/configuration/from-toml-file/main.go deleted file mode 100644 index b0c02935da..0000000000 --- a/_examples/configuration/from-toml-file/main.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - ctx.HTML("Hello!") - }) - // [...] - - // Good when you have two configurations, one for development and a different one for production use. - app.Listen(":8080", iris.WithConfiguration(iris.TOML("./configs/iris.tml"))) - - // or before run: - // app.Configure(iris.WithConfiguration(iris.TOML("./configs/iris.tml"))) - // app.Listen(":8080") -} diff --git a/_examples/configuration/from-yaml-file/configs/iris.yml b/_examples/configuration/from-yaml-file/configs/iris.yml deleted file mode 100644 index e68bbdf648..0000000000 --- a/_examples/configuration/from-yaml-file/configs/iris.yml +++ /dev/null @@ -1,16 +0,0 @@ -DisablePathCorrection: false -EnablePathEscape: false -FireMethodNotAllowed: true -DisableBodyConsumptionOnUnmarshal: true -TimeFormat: Mon, 01 Jan 2006 15:04:05 GMT -Charset: UTF-8 -SSLProxyHeaders: - X-Forwarded-Proto: https -HostProxyHeaders: - X-Host: true -RemoteAddrHeaders: - - X-Real-Ip - - X-Forwarded-For - - CF-Connecting-IP -Other: - Addr: :8080 diff --git a/_examples/configuration/from-yaml-file/main.go b/_examples/configuration/from-yaml-file/main.go deleted file mode 100644 index a872d5bd1c..0000000000 --- a/_examples/configuration/from-yaml-file/main.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - app.Get("/", func(ctx iris.Context) { - ctx.HTML("Hello!") - }) - // [...] - - // Good when you have two configurations, one for development and a different one for production use. - // If iris.YAML's input string argument is "~" then it loads the configuration from the home directory - // and can be shared between many iris instances. - cfg := iris.YAML("./configs/iris.yml") - addr := cfg.Other["Addr"].(string) - app.Listen(addr, iris.WithConfiguration(cfg)) - - // or before run: - // app.Configure(iris.WithConfiguration(iris.YAML("./configs/iris.yml"))) - // app.Listen(":8080") -} diff --git a/_examples/configuration/from-yaml-file/shared-configuration/main.go b/_examples/configuration/from-yaml-file/shared-configuration/main.go deleted file mode 100644 index 483cd7a56b..0000000000 --- a/_examples/configuration/from-yaml-file/shared-configuration/main.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - app.Get("/", func(ctx iris.Context) { - ctx.HTML("Hello!") - }) - // [...] - - // Good when you share configuration between multiple iris instances. - // This configuration file lives in your $HOME/iris.yml for unix hosts - // or %HOMEDRIVE%+%HOMEPATH%/iris.yml for windows hosts, and you can modify it. - app.Listen(":8080", iris.WithGlobalConfiguration) - // or before run: - // app.Configure(iris.WithGlobalConfiguration) - // app.Listen(":8080") -} diff --git a/_examples/configuration/functional/main.go b/_examples/configuration/functional/main.go deleted file mode 100644 index c5a3a47393..0000000000 --- a/_examples/configuration/functional/main.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - app.Get("/", func(ctx iris.Context) { - ctx.HTML("Hello!") - }) - // [...] - - // Good when you want to change some of the configuration's field. - // Prefix: "With", code editors will help you navigate through all - // configuration options without even a glitch to the documentation. - - app.Listen(":8080", iris.WithoutStartupLog, iris.WithCharset("utf-8")) - - // or before run: - // app.Configure(iris.WithoutStartupLog, iris.WithCharset("utf-8")) - // app.Listen(":8080") -} diff --git a/_examples/convert-handlers/negroni-like/main.go b/_examples/convert-handlers/negroni-like/main.go deleted file mode 100644 index be09c9e38c..0000000000 --- a/_examples/convert-handlers/negroni-like/main.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - irisMiddleware := iris.FromStd(negronilikeTestMiddleware) - app.Use(irisMiddleware) - - // Method GET: http://localhost:8080/ - app.Get("/", func(ctx iris.Context) { - ctx.HTML("

Home

") - // this will print an error, - // this route's handler will never be executed because the middleware's criteria not passed. - }) - - // Method GET: http://localhost:8080/ok - app.Get("/ok", func(ctx iris.Context) { - ctx.Writef("Hello world!") - // this will print "OK. Hello world!". - }) - - // http://localhost:8080 - // http://localhost:8080/ok - app.Listen(":8080") -} - -func negronilikeTestMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { - if r.URL.Path == "/ok" && r.Method == "GET" { - w.Write([]byte("OK. ")) - next(w, r) // go to the next route's handler - return - } - // else print an error and do not forward to the route's handler. - w.WriteHeader(iris.StatusBadRequest) - w.Write([]byte("Bad request")) -} diff --git a/_examples/convert-handlers/nethttp/main.go b/_examples/convert-handlers/nethttp/main.go deleted file mode 100644 index d78c14ee83..0000000000 --- a/_examples/convert-handlers/nethttp/main.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - irisMiddleware := iris.FromStd(nativeTestMiddleware) - app.Use(irisMiddleware) - - // Method GET: http://localhost:8080/ - app.Get("/", func(ctx iris.Context) { - ctx.HTML("Home") - }) - - // Method GET: http://localhost:8080/ok - app.Get("/ok", func(ctx iris.Context) { - ctx.HTML("Hello world!") - }) - - // http://localhost:8080 - // http://localhost:8080/ok - app.Listen(":8080") -} - -func nativeTestMiddleware(w http.ResponseWriter, r *http.Request) { - println("Request path: " + r.URL.Path) -} diff --git a/_examples/convert-handlers/real-usecase-raven/wrapping-the-router/main.go b/_examples/convert-handlers/real-usecase-raven/wrapping-the-router/main.go deleted file mode 100644 index 8abc1a530d..0000000000 --- a/_examples/convert-handlers/real-usecase-raven/wrapping-the-router/main.go +++ /dev/null @@ -1,45 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "net/http" - "runtime/debug" - - "github.com/kataras/iris/v12" - - "github.com/getsentry/raven-go" -) - -// https://docs.sentry.io/clients/go/integrations/http/ -func init() { - raven.SetDSN("https://:@sentry.io/") -} - -func main() { - app := iris.New() - app.Get("/", func(ctx iris.Context) { - ctx.Writef("Hi") - }) - - // Example for WrapRouter is already here: - // https://github.com/kataras/iris/blob/master/_examples/routing/custom-wrapper/main.go#L53 - app.WrapRouter(func(w http.ResponseWriter, r *http.Request, irisRouter http.HandlerFunc) { - // Exactly the same source code: - // https://github.com/getsentry/raven-go/blob/379f8d0a68ca237cf8893a1cdfd4f574125e2c51/http.go#L70 - - defer func() { - if rval := recover(); rval != nil { - debug.PrintStack() - rvalStr := fmt.Sprint(rval) - packet := raven.NewPacket(rvalStr, raven.NewException(errors.New(rvalStr), raven.NewStacktrace(2, 3, nil)), raven.NewHttp(r)) - raven.Capture(packet, nil) - w.WriteHeader(http.StatusInternalServerError) - } - }() - - irisRouter(w, r) - }) - - app.Listen(":8080") -} diff --git a/_examples/convert-handlers/real-usecase-raven/writing-middleware/main.go b/_examples/convert-handlers/real-usecase-raven/writing-middleware/main.go deleted file mode 100644 index 82ef598bca..0000000000 --- a/_examples/convert-handlers/real-usecase-raven/writing-middleware/main.go +++ /dev/null @@ -1,57 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "runtime/debug" - - "github.com/kataras/iris/v12" - - "github.com/getsentry/raven-go" -) - -// At this example you will see how to convert any net/http middleware -// that has the form of `(HandlerFunc) HandlerFunc`. -// If the `raven.RecoveryHandler` had the form of -// `(http.HandlerFunc)` or `(http.HandlerFunc, next http.HandlerFunc)` -// you could just use the `irisMiddleware := iris.FromStd(nativeHandler)` -// but it doesn't, however as you already know Iris can work with net/http directly -// because of the `ctx.ResponseWriter()` and `ctx.Request()` are the original -// http.ResponseWriter and *http.Request. -// (this one is a big advantage, as a result you can use Iris for ever :)). -// -// The source code of the native middleware does not change at all. -// https://github.com/getsentry/raven-go/blob/379f8d0a68ca237cf8893a1cdfd4f574125e2c51/http.go#L70 -// The only addition is the Line 18 and Line 39 (instead of handler(w,r)) -// and you have a new iris middleware ready to use! -func irisRavenMiddleware(ctx iris.Context) { - w, r := ctx.ResponseWriter(), ctx.Request() - - defer func() { - if rval := recover(); rval != nil { - debug.PrintStack() - rvalStr := fmt.Sprint(rval) - packet := raven.NewPacket(rvalStr, raven.NewException(errors.New(rvalStr), raven.NewStacktrace(2, 3, nil)), raven.NewHttp(r)) - raven.Capture(packet, nil) - w.WriteHeader(iris.StatusInternalServerError) - } - }() - - ctx.Next() -} - -// https://docs.sentry.io/clients/go/integrations/http/ -func init() { - raven.SetDSN("https://:@sentry.io/") -} - -func main() { - app := iris.New() - app.Use(irisRavenMiddleware) - - app.Get("/", func(ctx iris.Context) { - ctx.Writef("Hi") - }) - - app.Listen(":8080") -} diff --git a/_examples/cookies/basic/main.go b/_examples/cookies/basic/main.go deleted file mode 100644 index f9b789e360..0000000000 --- a/_examples/cookies/basic/main.go +++ /dev/null @@ -1,64 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func newApp() *iris.Application { - app := iris.New() - - // Set A Cookie. - app.Get("/cookies/{name}/{value}", func(ctx iris.Context) { - name := ctx.Params().Get("name") - value := ctx.Params().Get("value") - - ctx.SetCookieKV(name, value) // <-- - // Alternatively: ctx.SetCookie(&http.Cookie{...}) - // - // If you want to set custom the path: - // ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored")) - // - // If you want to be visible only to current request path: - // (note that client should be responsible for that if server sent an empty cookie's path, all browsers are compatible) - // ctx.SetCookieKV(name, value, iris.CookieCleanPath /* or iris.CookiePath("") */) - // More: - // iris.CookieExpires(time.Duration) - // iris.CookieHTTPOnly(false) - - ctx.Writef("cookie added: %s = %s", name, value) - }) - - // Retrieve A Cookie. - app.Get("/cookies/{name}", func(ctx iris.Context) { - name := ctx.Params().Get("name") - - value := ctx.GetCookie(name) // <-- - // If you want more than the value then: - // cookie, err := ctx.Request().Cookie(name) - // if err != nil { - // handle error. - // } - - ctx.WriteString(value) - }) - - // Delete A Cookie. - app.Delete("/cookies/{name}", func(ctx iris.Context) { - name := ctx.Params().Get("name") - - ctx.RemoveCookie(name) // <-- - // If you want to set custom the path: - // ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored")) - - ctx.Writef("cookie %s removed", name) - }) - - return app -} - -func main() { - app := newApp() - - // GET: http://localhost:8080/cookies/my_name/my_value - // GET: http://localhost:8080/cookies/my_name - // DELETE: http://localhost:8080/cookies/my_name - app.Listen(":8080") -} diff --git a/_examples/cookies/basic/main_test.go b/_examples/cookies/basic/main_test.go deleted file mode 100644 index 8dd9946701..0000000000 --- a/_examples/cookies/basic/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "fmt" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestCookiesBasic(t *testing.T) { - app := newApp() - e := httptest.New(t, app, httptest.URL("http://example.com")) - - cookieName, cookieValue := "my_cookie_name", "my_cookie_value" - - // Test set a Cookie. - t1 := e.GET(fmt.Sprintf("/cookies/%s/%s", cookieName, cookieValue)).Expect().Status(httptest.StatusOK) - t1.Cookie(cookieName).Value().Equal(cookieValue) // validate cookie's existence, it should be there now. - t1.Body().Contains(cookieValue) - - // Test retrieve a Cookie. - t2 := e.GET(fmt.Sprintf("/cookies/%s", cookieName)).Expect().Status(httptest.StatusOK) - t2.Body().Equal(cookieValue) - - // Test remove a Cookie. - t3 := e.DELETE(fmt.Sprintf("/cookies/%s", cookieName)).Expect().Status(httptest.StatusOK) - t3.Body().Contains(cookieName) - - t4 := e.GET(fmt.Sprintf("/cookies/%s", cookieName)).Expect().Status(httptest.StatusOK) - t4.Cookies().Empty() - t4.Body().Empty() -} diff --git a/_examples/cookies/options/main.go b/_examples/cookies/options/main.go deleted file mode 100644 index 70d7e2721a..0000000000 --- a/_examples/cookies/options/main.go +++ /dev/null @@ -1,76 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := newApp() - - // http://localhost:8080/set/name1/value1 - // http://localhost:8080/get/name1 - // http://localhost:8080/remove/name1 - app.Listen(":8080", iris.WithLogLevel("debug")) -} - -func newApp() *iris.Application { - app := iris.New() - app.Use(withCookieOptions) - - app.Get("/set/{name}/{value}", setCookie) - app.Get("/get/{name}", getCookie) - app.Get("/remove/{name}", removeCookie) - - return app -} - -func withCookieOptions(ctx iris.Context) { - // Register cookie options for request-lifecycle. - // To register per cookie, just add the CookieOption - // on the last variadic input argument of - // SetCookie, SetCookieKV, UpsertCookie, RemoveCookie - // and GetCookie Context methods. - // - // * CookieAllowReclaim - // * CookieAllowSubdomains - // * CookieSecure - // * CookieHTTPOnly - // * CookieSameSite - // * CookiePath - // * CookieCleanPath - // * CookieExpires - // * CookieEncoding - ctx.AddCookieOptions(iris.CookieAllowReclaim()) - ctx.Next() -} - -func setCookie(ctx iris.Context) { - name := ctx.Params().Get("name") - value := ctx.Params().Get("value") - - ctx.SetCookieKV(name, value) - - // By-default net/http does not remove or set the Cookie on the Request object. - // - // With the `CookieAllowReclaim` option, whenever you set or remove a cookie - // it will be also reflected in the Request object immediately (of the same request lifecycle) - // therefore, any of the next handlers in the chain are not holding the old value. - valueIsAvailableInRequestObject := ctx.GetCookie(name) - ctx.Writef("cookie %s=%s", name, valueIsAvailableInRequestObject) -} - -func getCookie(ctx iris.Context) { - name := ctx.Params().Get("name") - - value := ctx.GetCookie(name) - ctx.WriteString(value) -} - -func removeCookie(ctx iris.Context) { - name := ctx.Params().Get("name") - - ctx.RemoveCookie(name) - - removedFromRequestObject := ctx.GetCookie(name) // CookieAllowReclaim feature. - ctx.Writef("cookie %s removed, value should be empty=%s", name, removedFromRequestObject) -} diff --git a/_examples/cookies/options/main_test.go b/_examples/cookies/options/main_test.go deleted file mode 100644 index 7f064d466d..0000000000 --- a/_examples/cookies/options/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "fmt" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestCookieOptions(t *testing.T) { - app := newApp() - e := httptest.New(t, app, httptest.URL("http://example.com")) - - cookieName, cookieValue := "my_cookie_name", "my_cookie_value" - - // Test set a Cookie. - t1 := e.GET(fmt.Sprintf("/set/%s/%s", cookieName, cookieValue)).Expect().Status(httptest.StatusOK) - t1.Cookie(cookieName).Value().Equal(cookieValue) - t1.Body().Contains(fmt.Sprintf("%s=%s", cookieName, cookieValue)) - - // Test retrieve a Cookie. - t2 := e.GET(fmt.Sprintf("/get/%s", cookieName)).Expect().Status(httptest.StatusOK) - t2.Body().Equal(cookieValue) - - // Test remove a Cookie. - t3 := e.GET(fmt.Sprintf("/remove/%s", cookieName)).Expect().Status(httptest.StatusOK) - t3.Body().Contains(fmt.Sprintf("cookie %s removed, value should be empty=%s", cookieName, "")) - - t4 := e.GET(fmt.Sprintf("/get/%s", cookieName)).Expect().Status(httptest.StatusOK) - t4.Cookies().Empty() - t4.Body().Empty() -} diff --git a/_examples/cookies/securecookie/main.go b/_examples/cookies/securecookie/main.go deleted file mode 100644 index bca6e0a516..0000000000 --- a/_examples/cookies/securecookie/main.go +++ /dev/null @@ -1,72 +0,0 @@ -package main - -// developers can use any library to add a custom cookie encoder/decoder. -// At this example we use the gorilla's securecookie package: -// $ go get github.com/gorilla/securecookie -// $ go run main.go - -import ( - "github.com/kataras/iris/v12" - - "github.com/gorilla/securecookie" -) - -func main() { - app := newApp() - // http://localhost:8080/cookies/name/value - // http://localhost:8080/cookies/name - // http://localhost:8080/cookies/remove/name - app.Listen(":8080") -} - -func newApp() *iris.Application { - app := iris.New() - - r := app.Party("/cookies") - { - r.Use(useSecureCookies()) - - // Set A Cookie. - r.Get("/{name}/{value}", func(ctx iris.Context) { - name := ctx.Params().Get("name") - value := ctx.Params().Get("value") - - ctx.SetCookieKV(name, value) - - ctx.Writef("cookie added: %s = %s", name, value) - }) - - // Retrieve A Cookie. - r.Get("/{name}", func(ctx iris.Context) { - name := ctx.Params().Get("name") - - value := ctx.GetCookie(name) - - ctx.WriteString(value) - }) - - r.Get("/remove/{name}", func(ctx iris.Context) { - name := ctx.Params().Get("name") - - ctx.RemoveCookie(name) - - ctx.Writef("cookie %s removed", name) - }) - } - - return app -} - -func useSecureCookies() iris.Handler { - var ( - hashKey = securecookie.GenerateRandomKey(64) - blockKey = securecookie.GenerateRandomKey(32) - - s = securecookie.New(hashKey, blockKey) - ) - - return func(ctx iris.Context) { - ctx.AddCookieOptions(iris.CookieEncoding(s)) - ctx.Next() - } -} diff --git a/_examples/cookies/securecookie/main_test.go b/_examples/cookies/securecookie/main_test.go deleted file mode 100644 index 0754c9bd1f..0000000000 --- a/_examples/cookies/securecookie/main_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "fmt" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestSecureCookie(t *testing.T) { - app := newApp() - e := httptest.New(t, app, httptest.URL("http://example.com")) - - cookieName, cookieValue := "my_cookie_name", "my_cookie_value" - - // Test set a Cookie. - t1 := e.GET(fmt.Sprintf("/cookies/%s/%s", cookieName, cookieValue)).Expect().Status(httptest.StatusOK) - // note that this will not work because it doesn't always returns the same value: - // cookieValueEncoded, _ := sc.Encode(cookieName, cookieValue) - t1.Cookie(cookieName).Value().NotEqual(cookieValue) // validate cookie's existence and value is not on its raw form. - t1.Body().Contains(cookieValue) - - // Test retrieve a Cookie. - t2 := e.GET(fmt.Sprintf("/cookies/%s", cookieName)).Expect().Status(httptest.StatusOK) - t2.Body().Equal(cookieValue) - - // Test remove a Cookie. - t3 := e.GET(fmt.Sprintf("/cookies/remove/%s", cookieName)).Expect().Status(httptest.StatusOK) - t3.Body().Contains(cookieName) - - t4 := e.GET(fmt.Sprintf("/cookies/%s", cookieName)).Expect().Status(httptest.StatusOK) - t4.Cookies().Empty() - t4.Body().Empty() -} diff --git a/_examples/database/mongodb/.env b/_examples/database/mongodb/.env deleted file mode 100644 index c41f59a0ca..0000000000 --- a/_examples/database/mongodb/.env +++ /dev/null @@ -1,2 +0,0 @@ -PORT=8080 -DSN=mongodb://localhost:27017 \ No newline at end of file diff --git a/_examples/database/mongodb/0_create_movie.png b/_examples/database/mongodb/0_create_movie.png deleted file mode 100644 index 17e8a71e53..0000000000 Binary files a/_examples/database/mongodb/0_create_movie.png and /dev/null differ diff --git a/_examples/database/mongodb/1_update_movie.png b/_examples/database/mongodb/1_update_movie.png deleted file mode 100644 index d0482aa6b2..0000000000 Binary files a/_examples/database/mongodb/1_update_movie.png and /dev/null differ diff --git a/_examples/database/mongodb/2_get_all_movies.png b/_examples/database/mongodb/2_get_all_movies.png deleted file mode 100644 index 1360c0a813..0000000000 Binary files a/_examples/database/mongodb/2_get_all_movies.png and /dev/null differ diff --git a/_examples/database/mongodb/3_get_movie.png b/_examples/database/mongodb/3_get_movie.png deleted file mode 100644 index 71eca41ed0..0000000000 Binary files a/_examples/database/mongodb/3_get_movie.png and /dev/null differ diff --git a/_examples/database/mongodb/4_delete_movie.png b/_examples/database/mongodb/4_delete_movie.png deleted file mode 100644 index 185b730a71..0000000000 Binary files a/_examples/database/mongodb/4_delete_movie.png and /dev/null differ diff --git a/_examples/database/mongodb/Dockerfile b/_examples/database/mongodb/Dockerfile deleted file mode 100644 index dec864fef4..0000000000 --- a/_examples/database/mongodb/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# docker build -t myapp . -# docker run --rm -it -p 8080:8080 myapp:latest -FROM golang:latest AS builder -RUN apt-get update -ENV GO111MODULE=on \ - CGO_ENABLED=0 \ - GOOS=linux \ - GOARCH=amd64 -WORKDIR /go/src/app -COPY go.mod . -RUN go mod download -COPY . . -RUN go install - -FROM scratch -COPY --from=builder /go/bin/myapp . -ENTRYPOINT ["./myapp"] \ No newline at end of file diff --git a/_examples/database/mongodb/README.md b/_examples/database/mongodb/README.md deleted file mode 100644 index 4953dd1744..0000000000 --- a/_examples/database/mongodb/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# Build RESTful API with the official MongoDB Go Driver and Iris - -Article is coming soon, follow and stay tuned - -- -- - -Read [the fully functional example](main.go). - -## Run - -### Docker - -Install [Docker](https://www.docker.com/) and execute the command below - -```sh -$ docker-compose up -``` - -### Manually - -```sh -# .env file contents -PORT=8080 -DSN=mongodb://localhost:27017 -``` - -```sh -$ go run main.go -> 2019/01/28 05:17:59 Loading environment variables from file: .env -> 2019/01/28 05:17:59 ◽ Port=8080 -> 2019/01/28 05:17:59 ◽ DSN=mongodb://localhost:27017 -> Now listening on: http://localhost:8080 -``` - -```sh -GET : http://localhost:8080/api/store/movies -POST : http://localhost:8080/api/store/movies -GET : http://localhost:8080/api/store/movies/{id} -PUT : http://localhost:8080/api/store/movies/{id} -DELETE : http://localhost:8080/api/store/movies/{id} -``` - -## Screens - -### Add a Movie -![](0_create_movie.png) - -### Update a Movie - -![](1_update_movie.png) - -### Get all Movies - -![](2_get_all_movies.png) - -### Get a Movie by its ID - -![](3_get_movie.png) - -### Delete a Movie by its ID - -![](4_delete_movie.png) - diff --git a/_examples/database/mongodb/api/store/movie.go b/_examples/database/mongodb/api/store/movie.go deleted file mode 100644 index 3a2d9fb2c2..0000000000 --- a/_examples/database/mongodb/api/store/movie.go +++ /dev/null @@ -1,101 +0,0 @@ -package storeapi - -import ( - "myapp/httputil" - "myapp/store" - - "github.com/kataras/iris/v12" -) - -type MovieHandler struct { - service store.MovieService -} - -func NewMovieHandler(service store.MovieService) *MovieHandler { - return &MovieHandler{service: service} -} - -func (h *MovieHandler) GetAll(ctx iris.Context) { - movies, err := h.service.GetAll(nil) - if err != nil { - httputil.InternalServerErrorJSON(ctx, err, "Server was unable to retrieve all movies") - return - } - - if movies == nil { - // will return "null" if empty, with this "trick" we return "[]" json. - movies = make([]store.Movie, 0) - } - - ctx.JSON(movies) -} - -func (h *MovieHandler) Get(ctx iris.Context) { - id := ctx.Params().Get("id") - - m, err := h.service.GetByID(nil, id) - if err != nil { - if err == store.ErrNotFound { - ctx.NotFound() - } else { - httputil.InternalServerErrorJSON(ctx, err, "Server was unable to retrieve movie [%s]", id) - } - return - } - - ctx.JSON(m) -} - -func (h *MovieHandler) Add(ctx iris.Context) { - m := new(store.Movie) - - err := ctx.ReadJSON(m) - if err != nil { - httputil.FailJSON(ctx, iris.StatusBadRequest, err, "Malformed request payload") - return - } - - err = h.service.Create(nil, m) - if err != nil { - httputil.InternalServerErrorJSON(ctx, err, "Server was unable to create a movie") - return - } - - ctx.StatusCode(iris.StatusCreated) - ctx.JSON(m) -} - -func (h *MovieHandler) Update(ctx iris.Context) { - id := ctx.Params().Get("id") - - var m store.Movie - err := ctx.ReadJSON(&m) - if err != nil { - httputil.FailJSON(ctx, iris.StatusBadRequest, err, "Malformed request payload") - return - } - - err = h.service.Update(nil, id, m) - if err != nil { - if err == store.ErrNotFound { - ctx.NotFound() - return - } - httputil.InternalServerErrorJSON(ctx, err, "Server was unable to update movie [%s]", id) - return - } -} - -func (h *MovieHandler) Delete(ctx iris.Context) { - id := ctx.Params().Get("id") - - err := h.service.Delete(nil, id) - if err != nil { - if err == store.ErrNotFound { - ctx.NotFound() - return - } - httputil.InternalServerErrorJSON(ctx, err, "Server was unable to delete movie [%s]", id) - return - } -} diff --git a/_examples/database/mongodb/docker-compose.yml b/_examples/database/mongodb/docker-compose.yml deleted file mode 100644 index a9eec93ebd..0000000000 --- a/_examples/database/mongodb/docker-compose.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: "3.1" - -services: - app: - build: . - environment: - Port: 8080 - DSN: db:27017 - ports: - - 8080:8080 - depends_on: - - db - db: - image: mongo - environment: - MONGO_INITDB_DATABASE: store - ports: - - 27017:27017 diff --git a/_examples/database/mongodb/env/env.go b/_examples/database/mongodb/env/env.go deleted file mode 100644 index 9f735b4373..0000000000 --- a/_examples/database/mongodb/env/env.go +++ /dev/null @@ -1,85 +0,0 @@ -package env - -import ( - "fmt" - "log" - "os" - "path/filepath" - "strings" - - "github.com/joho/godotenv" -) - -var ( - // Port is the PORT environment variable or 8080 if missing. - // Used to open the tcp listener for our web server. - Port string - // DSN is the DSN environment variable or mongodb://localhost:27017 if missing. - // Used to connect to the mongodb. - DSN string -) - -func parse() { - Port = getDefault("PORT", "8080") - DSN = getDefault("DSN", "mongodb://localhost:27017") - - log.Printf("• Port=%s\n", Port) - log.Printf("• DSN=%s\n", DSN) -} - -// Load loads environment variables that are being used across the whole app. -// Loading from file(s), i.e .env or dev.env -// -// Example of a 'dev.env': -// PORT=8080 -// DSN=mongodb://localhost:27017 -// -// After `Load` the callers can get an environment variable via `os.Getenv`. -func Load(envFileName string) { - if args := os.Args; len(args) > 1 && args[1] == "help" { - fmt.Fprintln(os.Stderr, "https://github.com/kataras/iris/blob/master/_examples/database/mongodb/README.md") - os.Exit(-1) - } - - // If more than one filename passed with comma separated then load from all - // of these, a env file can be a partial too. - envFiles := strings.Split(envFileName, ",") - for _, envFile := range envFiles { - if filepath.Ext(envFile) == "" { - envFile += ".env" - } - - if fileExists(envFile) { - log.Printf("Loading environment variables from file: %s\n", envFile) - - if err := godotenv.Load(envFile); err != nil { - panic(fmt.Sprintf("error loading environment variables from [%s]: %v", envFile, err)) - } - } - } - - // envMap, _ := godotenv.Read(envFiles...) - // for k, v := range envMap { - // log.Printf("◽ %s=%s\n", k, v) - // } - - parse() -} - -func getDefault(key string, def string) string { - value := os.Getenv(key) - if value == "" { - os.Setenv(key, def) - value = def - } - - return value -} - -func fileExists(filename string) bool { - info, err := os.Stat(filename) - if os.IsNotExist(err) { - return false - } - return !info.IsDir() -} diff --git a/_examples/database/mongodb/go.mod b/_examples/database/mongodb/go.mod deleted file mode 100644 index 511a80007c..0000000000 --- a/_examples/database/mongodb/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module myapp - -go 1.15 - -require ( - github.com/joho/godotenv v1.3.0 - github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd - go.mongodb.org/mongo-driver v1.3.4 -) diff --git a/_examples/database/mongodb/httputil/error.go b/_examples/database/mongodb/httputil/error.go deleted file mode 100644 index 4bcc740fb8..0000000000 --- a/_examples/database/mongodb/httputil/error.go +++ /dev/null @@ -1,130 +0,0 @@ -package httputil - -import ( - "errors" - "fmt" - "io" - "net/http" - "os" - "runtime" - "runtime/debug" - "strings" - "time" - - "github.com/kataras/iris/v12" -) - -var validStackFuncs = []func(string) bool{ - func(file string) bool { - return strings.Contains(file, "/mongodb/api/") - }, -} - -// RuntimeCallerStack returns the app's `file:line` stacktrace -// to give more information about an error cause. -func RuntimeCallerStack() (s string) { - var pcs [10]uintptr - n := runtime.Callers(1, pcs[:]) - frames := runtime.CallersFrames(pcs[:n]) - - for { - frame, more := frames.Next() - for _, fn := range validStackFuncs { - if fn(frame.File) { - s += fmt.Sprintf("\n\t\t\t%s:%d", frame.File, frame.Line) - } - } - - if !more { - break - } - } - - return s -} - -// HTTPError describes an HTTP error. -type HTTPError struct { - error - Stack string `json:"-"` // the whole stacktrace. - CallerStack string `json:"-"` // the caller, file:lineNumber - When time.Time `json:"-"` // the time that the error occurred. - // ErrorCode int: maybe a collection of known error codes. - StatusCode int `json:"statusCode"` - // could be named as "reason" as well - // it's the message of the error. - Description string `json:"description"` -} - -func newError(statusCode int, err error, format string, args ...interface{}) HTTPError { - if format == "" { - format = http.StatusText(statusCode) - } - - desc := fmt.Sprintf(format, args...) - if err == nil { - err = errors.New(desc) - } - - return HTTPError{ - err, - string(debug.Stack()), - RuntimeCallerStack(), - time.Now(), - statusCode, - desc, - } -} - -func (err HTTPError) writeHeaders(ctx iris.Context) { - ctx.StatusCode(err.StatusCode) - ctx.Header("X-Content-Type-Options", "nosniff") -} - -// LogFailure will print out the failure to the "logger". -func LogFailure(logger io.Writer, ctx iris.Context, err HTTPError) { - timeFmt := err.When.Format("2006/01/02 15:04:05") - firstLine := fmt.Sprintf("%s %s: %s", timeFmt, http.StatusText(err.StatusCode), err.Error()) - whitespace := strings.Repeat(" ", len(timeFmt)+1) - fmt.Fprintf(logger, "%s\n%sIP: %s\n%sURL: %s\n%sSource: %s\n", - firstLine, whitespace, ctx.RemoteAddr(), whitespace, ctx.FullRequestURI(), whitespace, err.CallerStack) -} - -// Fail will send the status code, write the error's reason -// and return the HTTPError for further use, i.e logging, see `InternalServerError`. -func Fail(ctx iris.Context, statusCode int, err error, format string, args ...interface{}) HTTPError { - httpErr := newError(statusCode, err, format, args...) - httpErr.writeHeaders(ctx) - - ctx.WriteString(httpErr.Description) - return httpErr -} - -// FailJSON will send to the client the error data as JSON. -// Useful for APIs. -func FailJSON(ctx iris.Context, statusCode int, err error, format string, args ...interface{}) HTTPError { - httpErr := newError(statusCode, err, format, args...) - httpErr.writeHeaders(ctx) - - ctx.JSON(httpErr) - - return httpErr -} - -// InternalServerError logs to the server's terminal -// and dispatches to the client the 500 Internal Server Error. -// Internal Server errors are critical, so we log them to the `os.Stderr`. -func InternalServerError(ctx iris.Context, err error, format string, args ...interface{}) { - LogFailure(os.Stderr, ctx, Fail(ctx, iris.StatusInternalServerError, err, format, args...)) -} - -// InternalServerErrorJSON acts exactly like `InternalServerError` but instead it sends the data as JSON. -// Useful for APIs. -func InternalServerErrorJSON(ctx iris.Context, err error, format string, args ...interface{}) { - LogFailure(os.Stderr, ctx, FailJSON(ctx, iris.StatusInternalServerError, err, format, args...)) -} - -// UnauthorizedJSON sends JSON format of StatusUnauthorized(401) HTTPError value. -func UnauthorizedJSON(ctx iris.Context, err error, format string, args ...interface{}) HTTPError { - return FailJSON(ctx, iris.StatusUnauthorized, err, format, args...) -} diff --git a/_examples/database/mongodb/main.go b/_examples/database/mongodb/main.go deleted file mode 100644 index 185d08f686..0000000000 --- a/_examples/database/mongodb/main.go +++ /dev/null @@ -1,83 +0,0 @@ -package main - -// go get -u go.mongodb.org/mongo-driver -// go get -u github.com/joho/godotenv - -import ( - "context" - "flag" - "fmt" - "log" - "os" - - // APIs - storeapi "myapp/api/store" - - // - "myapp/env" - "myapp/store" - - "github.com/kataras/iris/v12" - - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" -) - -const version = "0.0.1" - -func init() { - envFileName := ".env" - - flagset := flag.CommandLine - flagset.StringVar(&envFileName, "env", envFileName, "the env file which web app will use to extract its environment variables") - flagset.Parse(os.Args[1:]) - - env.Load(envFileName) -} - -func main() { - clientOptions := options.Client().SetHosts([]string{env.DSN}) - client, err := mongo.Connect(context.Background(), clientOptions) - if err != nil { - log.Fatal(err) - } - - err = client.Ping(context.Background(), nil) - if err != nil { - log.Fatal(err) - } - defer client.Disconnect(context.TODO()) - - db := client.Database("store") - - var ( - // Collections. - moviesCollection = db.Collection("movies") - - // Services. - movieService = store.NewMovieService(moviesCollection) - ) - - app := iris.New() - app.Use(func(ctx iris.Context) { - ctx.Header("Server", "Iris MongoDB/"+version) - ctx.Next() - }) - - storeAPI := app.Party("/api/store") - { - movieHandler := storeapi.NewMovieHandler(movieService) - storeAPI.Get("/movies", movieHandler.GetAll) - storeAPI.Post("/movies", movieHandler.Add) - storeAPI.Get("/movies/{id}", movieHandler.Get) - storeAPI.Put("/movies/{id}", movieHandler.Update) - storeAPI.Delete("/movies/{id}", movieHandler.Delete) - } - - // GET: http://localhost:8080/api/store/movies - // POST: http://localhost:8080/api/store/movies - // GET: http://localhost:8080/api/store/movies/{id} - // PUT: http://localhost:8080/api/store/movies/{id} - // DELETE: http://localhost:8080/api/store/movies/{id} - app.Listen(fmt.Sprintf(":%s", env.Port), iris.WithOptimizations) -} diff --git a/_examples/database/mongodb/store/movie.go b/_examples/database/mongodb/store/movie.go deleted file mode 100644 index 133cddc912..0000000000 --- a/_examples/database/mongodb/store/movie.go +++ /dev/null @@ -1,180 +0,0 @@ -package store - -import ( - "context" - "errors" - - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" - "go.mongodb.org/mongo-driver/mongo" - // up to you: - // "go.mongodb.org/mongo-driver/mongo/options" -) - -type Movie struct { - ID primitive.ObjectID `json:"_id" bson:"_id"` /* you need the bson:"_id" to be able to retrieve with ID filled */ - Name string `json:"name"` - Cover string `json:"cover"` - Description string `json:"description"` -} - -type MovieService interface { - GetAll(ctx context.Context) ([]Movie, error) - GetByID(ctx context.Context, id string) (Movie, error) - Create(ctx context.Context, m *Movie) error - Update(ctx context.Context, id string, m Movie) error - Delete(ctx context.Context, id string) error -} - -type movieService struct { - C *mongo.Collection -} - -var _ MovieService = (*movieService)(nil) - -func NewMovieService(collection *mongo.Collection) MovieService { - // up to you: - // indexOpts := new(options.IndexOptions) - // indexOpts.SetName("movieIndex"). - // SetUnique(true). - // SetBackground(true). - // SetSparse(true) - - // collection.Indexes().CreateOne(context.Background(), mongo.IndexModel{ - // Keys: []string{"_id", "name"}, - // Options: indexOpts, - // }) - - return &movieService{C: collection} -} - -func (s *movieService) GetAll(ctx context.Context) ([]Movie, error) { - // Note: - // The mongodb's go-driver's docs says that you can pass `nil` to "find all" but this gives NilDocument error, - // probably it's a bug or a documentation's mistake, you have to pass `bson.D{}` instead. - cur, err := s.C.Find(ctx, bson.D{}) - if err != nil { - return nil, err - } - defer cur.Close(ctx) - - var results []Movie - - for cur.Next(ctx) { - if err = cur.Err(); err != nil { - return nil, err - } - - // elem := bson.D{} - var elem Movie - err = cur.Decode(&elem) - if err != nil { - return nil, err - } - - // results = append(results, Movie{ID: elem[0].Value.(primitive.ObjectID)}) - - results = append(results, elem) - } - - return results, nil -} - -func matchID(id string) (bson.D, error) { - objectID, err := primitive.ObjectIDFromHex(id) - if err != nil { - return nil, err - } - - filter := bson.D{{Key: "_id", Value: objectID}} - return filter, nil -} - -var ErrNotFound = errors.New("not found") - -func (s *movieService) GetByID(ctx context.Context, id string) (Movie, error) { - var movie Movie - filter, err := matchID(id) - if err != nil { - return movie, err - } - - err = s.C.FindOne(ctx, filter).Decode(&movie) - if err == mongo.ErrNoDocuments { - return movie, ErrNotFound - } - return movie, err -} - -func (s *movieService) Create(ctx context.Context, m *Movie) error { - if m.ID.IsZero() { - m.ID = primitive.NewObjectID() - } - - _, err := s.C.InsertOne(ctx, m) - if err != nil { - return err - } - - // The following doesn't work if you have the `bson:"_id` on Movie.ID field, - // therefore we manually generate a new ID (look above). - // res, err := ...InsertOne - // objectID := res.InsertedID.(primitive.ObjectID) - // m.ID = objectID - return nil -} - -func (s *movieService) Update(ctx context.Context, id string, m Movie) error { - filter, err := matchID(id) - if err != nil { - return err - } - - // update := bson.D{ - // {Key: "$set", Value: m}, - // } - // ^ this will override all fields, you can do that, depending on your design. but let's check each field: - elem := bson.D{} - - if m.Name != "" { - elem = append(elem, bson.E{Key: "name", Value: m.Name}) - } - - if m.Description != "" { - elem = append(elem, bson.E{Key: "description", Value: m.Description}) - } - - if m.Cover != "" { - elem = append(elem, bson.E{Key: "cover", Value: m.Cover}) - } - - update := bson.D{ - {Key: "$set", Value: elem}, - } - - _, err = s.C.UpdateOne(ctx, filter, update) - if err != nil { - if err == mongo.ErrNoDocuments { - return ErrNotFound - } - return err - } - - return nil -} - -func (s *movieService) Delete(ctx context.Context, id string) error { - filter, err := matchID(id) - if err != nil { - return err - } - _, err = s.C.DeleteOne(ctx, filter) - if err != nil { - if err == mongo.ErrNoDocuments { - return ErrNotFound - } - return err - } - - return nil -} diff --git a/_examples/database/mysql/Dockerfile b/_examples/database/mysql/Dockerfile deleted file mode 100644 index dec864fef4..0000000000 --- a/_examples/database/mysql/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# docker build -t myapp . -# docker run --rm -it -p 8080:8080 myapp:latest -FROM golang:latest AS builder -RUN apt-get update -ENV GO111MODULE=on \ - CGO_ENABLED=0 \ - GOOS=linux \ - GOARCH=amd64 -WORKDIR /go/src/app -COPY go.mod . -RUN go mod download -COPY . . -RUN go install - -FROM scratch -COPY --from=builder /go/bin/myapp . -ENTRYPOINT ["./myapp"] \ No newline at end of file diff --git a/_examples/database/mysql/README.md b/_examples/database/mysql/README.md deleted file mode 100644 index 2575522616..0000000000 --- a/_examples/database/mysql/README.md +++ /dev/null @@ -1,146 +0,0 @@ -# Iris, MySQL, Groupcache & Docker Example - -## 📘 Endpoints - -| Method | Path | Description | URL Parameters | Body | Auth Required | -|--------|---------------------|------------------------|--------------- |----------------------------|---------------| -| ANY | /token | Prints a new JWT Token | - | - | - | -| GET | /category | Lists a set of Categories | offset, limit, order | - | - | -| POST | /category | Creates a Category | - | JSON [Full Category](migration/api_category/create_category.json) | Token | -| PUT | /category | Fully-Updates a Category | - | JSON [Full Category](migration/api_category/update_category.json) | Token | -| PATCH | /category/{id} | Partially-Updates a Category | - | JSON [Partial Category](migration/api_category/update_partial_category.json) | Token | -| GET | /category/{id} | Prints a Category | - | - | - | -| DELETE | /category/{id} | Deletes a Category | - | - | Token | -| GET | /category/{id}/products | Lists all Products from a Category | offset, limit, order | - | - | -| POST | /category/{id}/products | (Batch) Assigns one or more Products to a Category | - | JSON [Products](migration/api_category/insert_products_category.json) | Token | -| GET | /product | Lists a set of Products (cache) | offset, limit, order | - | - | -| POST | /product | Creates a Product | - | JSON [Full Product](migration/api_product/create_product.json) | Token | -| PUT | /product | Fully-Updates a Product | - | JSON [Full Product](migration/api_product/update_product.json) | Token | -| PATCH | /product/{id} | Partially-Updates a Product | - | JSON [Partial Product](migration/api_product/update_partial_product.json) | Token | -| GET | /product/{id} | Prints a Product (cache) | - | - | - | -| DELETE | /product/{id} | Deletes a Product | - | - | Token | - - - -## 📑 Responses - -* **Content-Type** of `"application/json;charset=utf-8"`, snake_case naming (identical to the database columns) -* **Status Codes** - * 500 for server(db) errors, - * 422 for validation errors, e.g. - ```json - { - "code": 422, - "message": "required fields are missing", - "timestamp": 1589306271 - } - ``` - * 400 for malformed syntax, e.g. - ```json - { - "code": 400, - "message": "json: cannot unmarshal number -2 into Go struct field Category.position of type uint64", - "timestamp": 1589306325 - } - ``` - ```json - { - "code": 400, - "message": "json: unknown field \"field_not_exists\"", - "timestamp": 1589306367 - } - ``` - * 404 for entity not found, e.g. - ```json - { - "code": 404, - "message": "entity does not exist", - "timestamp": 1589306199 - } - ``` - * 304 for unaffected UPDATE or DELETE, - * 201 for CREATE with the last inserted ID, - * 200 for GET, UPDATE and DELETE - -## ⚡ Get Started - -Download the folder. - -### Install (Docker) - -Install [Docker](https://www.docker.com/) and execute the command below - -```sh -$ docker-compose up -``` - -### Install (Manually) - -Run `go build` or `go run main.go` and read below. - -#### MySQL - -Environment variables: - -```sh -MYSQL_USER=user_myapp -MYSQL_PASSWORD=dbpassword -MYSQL_HOST=localhost -MYSQL_DATABASE=myapp -``` - -Download the schema from [migration/myapp.sql](migration/myapp.sql) and execute it against your MySQL server instance. - -```sql -CREATE DATABASE IF NOT EXISTS myapp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - -USE myapp; - -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - -DROP TABLE IF EXISTS categories; -CREATE TABLE categories ( - id int(11) NOT NULL AUTO_INCREMENT, - title varchar(255) NOT NULL, - position int(11) NOT NULL, - image_url varchar(255) NOT NULL, - created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (id) -); - -DROP TABLE IF EXISTS products; -CREATE TABLE products ( - id int(11) NOT NULL AUTO_INCREMENT, - category_id int, - title varchar(255) NOT NULL, - image_url varchar(255) NOT NULL, - price decimal(10,2) NOT NULL, - description text NOT NULL, - created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (id), - FOREIGN KEY (category_id) REFERENCES categories(id) -); - -SET FOREIGN_KEY_CHECKS = 1; -``` - -### Requests - -Some request bodies can be found at: [migration/api_category](migration/api_category) and [migration/api_product](migration/api_product). **However** I've provided a [postman.json](migration/myapp_postman.json) Collection that you can import to your [POSTMAN](https://learning.postman.com/docs/postman/collections/importing-and-exporting-data/#collections) and start playing with the API. - -All write-access endpoints are "protected" via JWT, a client should "verify" itself. You'll need to manually take the **token** from the `http://localhost:8080/token` and put it on url parameter `?token=$token` or to the `Authentication: Bearer $token` request header. - -### Unit or End-To-End Testing? - -Testing is important. The code is written in a way that testing should be trivial (Pseudo/memory Database or SQLite local file could be integrated as well, for end-to-end tests a Docker image with MySQL and fire tests against that server). However, there is [nothing(?)](service/category_service_test.go) to see here. - -## Packages - -- https://github.com/dgrijalva/jwt-go (JWT parsing) -- https://github.com/go-sql-driver/mysql (Go Driver for MySQL) -- https://github.com/DATA-DOG/go-sqlmock (Testing DB see [service/category_service_test.go](service/category_service_test.go)) -- https://github.com/kataras/iris (HTTP) -- https://github.com/mailgun/groupcache (Caching) diff --git a/_examples/database/mysql/api/api.go b/_examples/database/mysql/api/api.go deleted file mode 100644 index 2691fdf4cf..0000000000 --- a/_examples/database/mysql/api/api.go +++ /dev/null @@ -1,97 +0,0 @@ -// Package api contains the handlers for our HTTP Endpoints. -package api - -import ( - "time" - - "myapp/service" - "myapp/sql" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/jwt" - "github.com/kataras/iris/v12/middleware/requestid" -) - -// Router accepts any required dependencies and returns the main server's handler. -func Router(db sql.Database, secret string) func(iris.Party) { - return func(r iris.Party) { - j := jwt.HMAC(15*time.Minute, secret) - - r.Use(requestid.New()) - r.Use(verifyToken(j)) - // Generate a token for testing by navigating to - // http://localhost:8080/token endpoint. - // Copy-paste it to a ?token=$token url parameter or - // open postman and put an Authentication: Bearer $token to get - // access on create, update and delete endpoinds. - - r.Get("/token", writeToken(j)) - - var ( - categoryService = service.NewCategoryService(db) - productService = service.NewProductService(db) - ) - - cat := r.Party("/category") - { - // TODO: new Use to add middlewares to specific - // routes per METHOD ( we already have the per path through parties.) - handler := NewCategoryHandler(categoryService) - - cat.Get("/", handler.List) - cat.Post("/", handler.Create) - cat.Put("/", handler.Update) - - cat.Get("/{id:int64}", handler.GetByID) - cat.Patch("/{id:int64}", handler.PartialUpdate) - cat.Delete("/{id:int64}", handler.Delete) - /* You can also do something like that: - cat.PartyFunc("/{id:int64}", func(c iris.Party) { - c.Get("/", handler.GetByID) - c.Post("/", handler.PartialUpdate) - c.Delete("/", handler.Delete) - }) - */ - - cat.Get("/{id:int64}/products", handler.ListProducts) - cat.Post("/{id:int64}/products", handler.InsertProducts(productService)) - } - - prod := r.Party("/product") - { - handler := NewProductHandler(productService) - - prod.Get("/", handler.List) - prod.Post("/", handler.Create) - prod.Put("/", handler.Update) - - prod.Get("/{id:int64}", handler.GetByID) - prod.Patch("/{id:int64}", handler.PartialUpdate) - prod.Delete("/{id:int64}", handler.Delete) - } - - } -} - -func writeToken(j *jwt.JWT) iris.Handler { - return func(ctx iris.Context) { - claims := jwt.Claims{ - Issuer: "https://iris-go.com", - Audience: jwt.Audience{requestid.Get(ctx)}, - } - - j.WriteToken(ctx, claims) - } -} - -func verifyToken(j *jwt.JWT) iris.Handler { - return func(ctx iris.Context) { - // Allow all GET. - if ctx.Method() == iris.MethodGet { - ctx.Next() - return - } - - j.Verify(ctx) - } -} diff --git a/_examples/database/mysql/api/category_handler.go b/_examples/database/mysql/api/category_handler.go deleted file mode 100644 index 7e8c3f8425..0000000000 --- a/_examples/database/mysql/api/category_handler.go +++ /dev/null @@ -1,251 +0,0 @@ -package api - -import ( - "myapp/entity" - "myapp/service" - "myapp/sql" - - "github.com/kataras/iris/v12" -) - -// CategoryHandler is the http mux for categories. -type CategoryHandler struct { - // [...options] - - service *service.CategoryService -} - -// NewCategoryHandler returns the main controller for the categories API. -func NewCategoryHandler(service *service.CategoryService) *CategoryHandler { - return &CategoryHandler{service} -} - -// GetByID fetches a single record from the database and sends it to the client. -// Method: GET. -func (h *CategoryHandler) GetByID(ctx iris.Context) { - id := ctx.Params().GetInt64Default("id", 0) - - var cat entity.Category - err := h.service.GetByID(ctx.Request().Context(), &cat, id) - if err != nil { - if err == sql.ErrNoRows { - writeEntityNotFound(ctx) - return - } - - debugf("CategoryHandler.GetByID(id=%d): %v", id, err) - writeInternalServerError(ctx) - return - } - - ctx.JSON(cat) -} - -/* - -type ( - List struct { - Data interface{} `json:"data"` - Order string `json:"order"` - Next Range `json:"next,omitempty"` - Prev Range `json:"prev,omitempty"` - } - - Range struct { - Offset int64 `json:"offset"` - Limit int64 `json:"limit` - } -) -*/ - -// List lists a set of records from the database. -// Method: GET. -func (h *CategoryHandler) List(ctx iris.Context) { - q := ctx.Request().URL.Query() - opts := sql.ParseListOptions(q) - - // initialize here in order to return an empty json array `[]` instead of `null`. - categories := entity.Categories{} - err := h.service.List(ctx.Request().Context(), &categories, opts) - if err != nil && err != sql.ErrNoRows { - debugf("CategoryHandler.List(DB) (limit=%d offset=%d where=%s=%v): %v", - opts.Limit, opts.Offset, opts.WhereColumn, opts.WhereValue, err) - - writeInternalServerError(ctx) - return - } - - ctx.JSON(categories) -} - -// Create adds a record to the database. -// Method: POST. -func (h *CategoryHandler) Create(ctx iris.Context) { - var cat entity.Category - if err := ctx.ReadJSON(&cat); err != nil { - return - } - - id, err := h.service.Insert(ctx.Request().Context(), cat) - if err != nil { - if err == sql.ErrUnprocessable { - ctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), "required fields are missing")) - return - } - - debugf("CategoryHandler.Create(DB): %v", err) - writeInternalServerError(ctx) - return - } - - // Send 201 with body of {"id":$last_inserted_id"}. - ctx.StatusCode(iris.StatusCreated) - ctx.JSON(iris.Map{cat.PrimaryKey(): id}) -} - -// Update performs a full-update of a record in the database. -// Method: PUT. -func (h *CategoryHandler) Update(ctx iris.Context) { - var cat entity.Category - if err := ctx.ReadJSON(&cat); err != nil { - return - } - - affected, err := h.service.Update(ctx.Request().Context(), cat) - if err != nil { - if err == sql.ErrUnprocessable { - ctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), "required fields are missing")) - return - } - - debugf("CategoryHandler.Update(DB): %v", err) - writeInternalServerError(ctx) - return - } - - status := iris.StatusOK - if affected == 0 { - status = iris.StatusNotModified - } - - ctx.StatusCode(status) -} - -// PartialUpdate is the handler for partially update one or more fields of the record. -// Method: PATCH. -func (h *CategoryHandler) PartialUpdate(ctx iris.Context) { - id := ctx.Params().GetInt64Default("id", 0) - - var attrs map[string]interface{} - if err := ctx.ReadJSON(&attrs); err != nil { - return - } - - affected, err := h.service.PartialUpdate(ctx.Request().Context(), id, attrs) - if err != nil { - if err == sql.ErrUnprocessable { - ctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), "unsupported value(s)")) - return - } - - debugf("CategoryHandler.PartialUpdate(DB): %v", err) - writeInternalServerError(ctx) - return - } - - status := iris.StatusOK - if affected == 0 { - status = iris.StatusNotModified - } - - ctx.StatusCode(status) -} - -// Delete removes a record from the database. -// Method: DELETE. -func (h *CategoryHandler) Delete(ctx iris.Context) { - id := ctx.Params().GetInt64Default("id", 0) - - affected, err := h.service.DeleteByID(ctx.Request().Context(), id) - if err != nil { - debugf("CategoryHandler.Delete(DB): %v", err) - writeInternalServerError(ctx) - return - } - - status := iris.StatusOK // StatusNoContent - if affected == 0 { - status = iris.StatusNotModified - } - - ctx.StatusCode(status) -} - -// Products. - -// ListProducts lists products of a Category. -// Example: from cheap to expensive: -// http://localhost:8080/category/3/products?offset=0&limit=30&by=price&order=asc -// Method: GET. -func (h *CategoryHandler) ListProducts(ctx iris.Context) { - id := ctx.Params().GetInt64Default("id", 0) - - // NOTE: could add cache here too. - - q := ctx.Request().URL.Query() - opts := sql.ParseListOptions(q).Where("category_id", id) - opts.Table = "products" - if opts.OrderByColumn == "" { - opts.OrderByColumn = "updated_at" - } - - var products entity.Products - err := h.service.List(ctx.Request().Context(), &products, opts) - if err != nil { - debugf("CategoryHandler.ListProducts(DB) (table=%s where=%s=%v limit=%d offset=%d): %v", - opts.Table, opts.WhereColumn, opts.WhereValue, opts.Limit, opts.Offset, err) - - writeInternalServerError(ctx) - return - } - - ctx.JSON(products) -} - -// InsertProducts assigns new products to a Category (accepts a list of products). -// Method: POST. -func (h *CategoryHandler) InsertProducts(productService *service.ProductService) iris.Handler { - return func(ctx iris.Context) { - categoryID := ctx.Params().GetInt64Default("id", 0) - - var products []entity.Product - if err := ctx.ReadJSON(&products); err != nil { - return - } - - for i := range products { - products[i].CategoryID = categoryID - } - - inserted, err := productService.BatchInsert(ctx.Request().Context(), products) - if err != nil { - if err == sql.ErrUnprocessable { - ctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), "required fields are missing")) - return - } - - debugf("CategoryHandler.InsertProducts(DB): %v", err) - writeInternalServerError(ctx) - return - } - - if inserted == 0 { - ctx.StatusCode(iris.StatusNotModified) - return - } - - // Send 201 with body of {"inserted":$inserted"}. - ctx.StatusCode(iris.StatusCreated) - ctx.JSON(iris.Map{"inserted": inserted}) - } -} diff --git a/_examples/database/mysql/api/helper.go b/_examples/database/mysql/api/helper.go deleted file mode 100644 index 8ee14c0fa5..0000000000 --- a/_examples/database/mysql/api/helper.go +++ /dev/null @@ -1,25 +0,0 @@ -package api - -import ( - "log" - - "github.com/kataras/iris/v12" -) - -const debug = true - -func debugf(format string, args ...interface{}) { - if !debug { - return - } - - log.Printf(format, args...) -} - -func writeInternalServerError(ctx iris.Context) { - ctx.StopWithJSON(iris.StatusInternalServerError, newError(iris.StatusInternalServerError, ctx.Request().Method, ctx.Path(), "")) -} - -func writeEntityNotFound(ctx iris.Context) { - ctx.StopWithJSON(iris.StatusNotFound, newError(iris.StatusNotFound, ctx.Request().Method, ctx.Path(), "entity does not exist")) -} diff --git a/_examples/database/mysql/api/httperror.go b/_examples/database/mysql/api/httperror.go deleted file mode 100644 index 7b599eeb34..0000000000 --- a/_examples/database/mysql/api/httperror.go +++ /dev/null @@ -1,60 +0,0 @@ -package api - -import ( - "fmt" - "time" - - "github.com/kataras/iris/v12" -) - -// Error holds the error sent by server to clients (JSON). -type Error struct { - StatusCode int `json:"code"` - Method string `json:"-"` - Path string `json:"-"` - Message string `json:"message"` - Timestamp int64 `json:"timestamp"` -} - -func newError(statusCode int, method, path, format string, args ...interface{}) Error { - msg := format - if len(args) > 0 { - // why we check for that? If the original error message came from our database - // and it contains fmt-reserved words - // like %s or %d we will get MISSING(=...) in our error message and we don't want that. - msg = fmt.Sprintf(msg, args...) - } - - if msg == "" { - msg = iris.StatusText(statusCode) - } - - return Error{ - StatusCode: statusCode, - Method: method, - Path: path, - Message: msg, - Timestamp: time.Now().Unix(), - } -} - -// Error implements the internal Go error interface. -func (e Error) Error() string { - return fmt.Sprintf("[%d] %s: %s: %s", e.StatusCode, e.Method, e.Path, e.Message) -} - -// Is implements the standard `errors.Is` internal interface. -// Usage: errors.Is(e, target) -func (e Error) Is(target error) bool { - if target == nil { - return false - } - - err, ok := target.(Error) - if !ok { - return false - } - - return (err.StatusCode == e.StatusCode || e.StatusCode == 0) && - (err.Message == e.Message || e.Message == "") -} diff --git a/_examples/database/mysql/api/middleware/.gitkeep b/_examples/database/mysql/api/middleware/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/_examples/database/mysql/api/product_handler.go b/_examples/database/mysql/api/product_handler.go deleted file mode 100644 index 31250bf313..0000000000 --- a/_examples/database/mysql/api/product_handler.go +++ /dev/null @@ -1,173 +0,0 @@ -package api - -import ( - "time" - - "myapp/cache" - "myapp/entity" - "myapp/service" - "myapp/sql" - - "github.com/kataras/iris/v12" -) - -// ProductHandler is the http mux for products. -type ProductHandler struct { - service *service.ProductService - cache *cache.Cache -} - -// NewProductHandler returns the main controller for the products API. -func NewProductHandler(service *service.ProductService) *ProductHandler { - return &ProductHandler{ - service: service, - cache: cache.New(service, "products", time.Minute), - } -} - -// GetByID fetches a single record from the database and sends it to the client. -// Method: GET. -func (h *ProductHandler) GetByID(ctx iris.Context) { - id := ctx.Params().GetString("id") - - var product []byte - err := h.cache.GetByID(ctx.Request().Context(), id, &product) - if err != nil { - if err == sql.ErrNoRows { - writeEntityNotFound(ctx) - return - } - - debugf("ProductHandler.GetByID(id=%v): %v", id, err) - writeInternalServerError(ctx) - return - } - - ctx.ContentType("application/json") - ctx.Write(product) - - // ^ Could use our simple `noCache` or implement a Cache-Control (see kataras/iris/cache for that) - // but let's keep it simple. -} - -// List lists a set of records from the database. -// Method: GET. -func (h *ProductHandler) List(ctx iris.Context) { - key := ctx.Request().URL.RawQuery - - products := []byte("[]") - err := h.cache.List(ctx.Request().Context(), key, &products) - if err != nil && err != sql.ErrNoRows { - debugf("ProductHandler.List(DB) (%s): %v", - key, err) - - writeInternalServerError(ctx) - return - } - - ctx.ContentType("application/json") - ctx.Write(products) -} - -// Create adds a record to the database. -// Method: POST. -func (h *ProductHandler) Create(ctx iris.Context) { - var product entity.Product - if err := ctx.ReadJSON(&product); err != nil { - return - } - - id, err := h.service.Insert(ctx.Request().Context(), product) - if err != nil { - if err == sql.ErrUnprocessable { - ctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), "required fields are missing")) - return - } - - debugf("ProductHandler.Create(DB): %v", err) - writeInternalServerError(ctx) - return - } - - // Send 201 with body of {"id":$last_inserted_id"}. - ctx.StatusCode(iris.StatusCreated) - ctx.JSON(iris.Map{product.PrimaryKey(): id}) -} - -// Update performs a full-update of a record in the database. -// Method: PUT. -func (h *ProductHandler) Update(ctx iris.Context) { - var product entity.Product - if err := ctx.ReadJSON(&product); err != nil { - return - } - - affected, err := h.service.Update(ctx.Request().Context(), product) - if err != nil { - if err == sql.ErrUnprocessable { - ctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), "required fields are missing")) - return - } - - debugf("ProductHandler.Update(DB): %v", err) - writeInternalServerError(ctx) - return - } - - status := iris.StatusOK - if affected == 0 { - status = iris.StatusNotModified - } - - ctx.StatusCode(status) -} - -// PartialUpdate is the handler for partially update one or more fields of the record. -// Method: PATCH. -func (h *ProductHandler) PartialUpdate(ctx iris.Context) { - id := ctx.Params().GetInt64Default("id", 0) - - var attrs map[string]interface{} - if err := ctx.ReadJSON(&attrs); err != nil { - return - } - - affected, err := h.service.PartialUpdate(ctx.Request().Context(), id, attrs) - if err != nil { - if err == sql.ErrUnprocessable { - ctx.StopWithJSON(iris.StatusUnprocessableEntity, newError(iris.StatusUnprocessableEntity, ctx.Request().Method, ctx.Path(), "unsupported value(s)")) - return - } - - debugf("ProductHandler.PartialUpdate(DB): %v", err) - writeInternalServerError(ctx) - return - } - - status := iris.StatusOK - if affected == 0 { - status = iris.StatusNotModified - } - - ctx.StatusCode(status) -} - -// Delete removes a record from the database. -// Method: DELETE. -func (h *ProductHandler) Delete(ctx iris.Context) { - id := ctx.Params().GetInt64Default("id", 0) - - affected, err := h.service.DeleteByID(ctx.Request().Context(), id) - if err != nil { - debugf("ProductHandler.Delete(DB): %v", err) - writeInternalServerError(ctx) - return - } - - status := iris.StatusOK // StatusNoContent - if affected == 0 { - status = iris.StatusNotModified - } - - ctx.StatusCode(status) -} diff --git a/_examples/database/mysql/cache/groupcache.go b/_examples/database/mysql/cache/groupcache.go deleted file mode 100644 index 4fb4e30db4..0000000000 --- a/_examples/database/mysql/cache/groupcache.go +++ /dev/null @@ -1,120 +0,0 @@ -package cache - -import ( - "context" - "encoding/json" - "net/url" - "strconv" - "time" - - "myapp/entity" - "myapp/sql" - - "github.com/mailgun/groupcache/v2" -) - -// Service that cache will use to retrieve data. -type Service interface { - RecordInfo() sql.Record - GetByID(ctx context.Context, dest interface{}, id int64) error - List(ctx context.Context, dest interface{}, opts sql.ListOptions) error -} - -// Cache is a simple structure which holds the groupcache and the database service, exposes -// `GetByID` and `List` which returns cached (or stores new) items. -type Cache struct { - service Service - maxAge time.Duration - group *groupcache.Group -} - -// Size default size to use on groupcache, defaults to 3MB. -var Size int64 = 3 << (10 * 3) - -// New returns a new cache service which exposes `GetByID` and `List` methods to work with. -// The "name" should be unique, "maxAge" for cache expiration. -func New(service Service, name string, maxAge time.Duration) *Cache { - c := new(Cache) - c.service = service - c.maxAge = maxAge - c.group = groupcache.NewGroup(name, Size, c) - return c -} - -const ( - prefixID = "#" - prefixList = "[" -) - -// Get implements the groupcache.Getter interface. -// Use `GetByID` and `List` instead. -func (c *Cache) Get(ctx context.Context, key string, dest groupcache.Sink) error { - if len(key) < 2 { // empty or missing prefix+key, should never happen. - return sql.ErrUnprocessable - } - - var v interface{} - - prefix := key[0:1] - key = key[1:] - switch prefix { - case prefixID: - // Get by ID. - id, err := strconv.ParseInt(key, 10, 64) - if err != nil || id <= 0 { - return err - } - - switch c.service.RecordInfo().(type) { - case *entity.Category: - v = new(entity.Category) - case *entity.Product: - v = new(entity.Product) - } - - err = c.service.GetByID(ctx, v, id) - if err != nil { - return err - } - - case prefixList: - // Get a set of records, list. - q, err := url.ParseQuery(key) - if err != nil { - return err - } - opts := sql.ParseListOptions(q) - - switch c.service.RecordInfo().(type) { - case *entity.Category: - v = new(entity.Categories) - case *entity.Product: - v = new(entity.Products) - } - - err = c.service.List(ctx, v, opts) - if err != nil { - return err - } - - default: - return sql.ErrUnprocessable - } - - b, err := json.Marshal(v) - if err != nil { - return err - } - - return dest.SetBytes(b, time.Now().Add(c.maxAge)) -} - -// GetByID binds an item to "dest" an item based on its "id". -func (c *Cache) GetByID(ctx context.Context, id string, dest *[]byte) error { - return c.group.Get(ctx, prefixID+id, groupcache.AllocatingByteSliceSink(dest)) -} - -// List binds item to "dest" based on the "rawQuery" of `url.Values` for `ListOptions`. -func (c *Cache) List(ctx context.Context, rawQuery string, dest *[]byte) error { - return c.group.Get(ctx, prefixList+rawQuery, groupcache.AllocatingByteSliceSink(dest)) -} diff --git a/_examples/database/mysql/docker-compose.yml b/_examples/database/mysql/docker-compose.yml deleted file mode 100644 index 715a20d415..0000000000 --- a/_examples/database/mysql/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: '3.1' - -services: - db: - image: mysql - command: --default-authentication-plugin=mysql_native_password - environment: - MYSQL_ROOT_PASSWORD: dbpassword - MYSQL_DATABASE: myapp - MYSQL_USER: user_myapp - MYSQL_PASSWORD: dbpassword - tty: true - volumes: - - ./migration:/docker-entrypoint-initdb.d - app: - build: . - ports: - - 8080:8080 - environment: - PORT: 8080 - MYSQL_USER: user_myapp - MYSQL_PASSWORD: dbpassword - MYSQL_DATABASE: myapp - MYSQL_HOST: db - restart: on-failure - healthcheck: - test: ["CMD", "curl", "-f", "tcp://db:3306"] - interval: 30s - timeout: 10s - retries: 5 - depends_on: - - db \ No newline at end of file diff --git a/_examples/database/mysql/entity/category.go b/_examples/database/mysql/entity/category.go deleted file mode 100644 index 61dae29faf..0000000000 --- a/_examples/database/mysql/entity/category.go +++ /dev/null @@ -1,89 +0,0 @@ -package entity - -import ( - "database/sql" - "time" -) - -// Category represents the categories entity. -// Each product belongs to a category, see `Product.CategoryID` field. -// It implements the `sql.Record` and `sql.Sorted` interfaces. -type Category struct { - ID int64 `db:"id" json:"id"` - Title string `db:"title" json:"title"` - Position uint64 `db:"position" json:"position"` - ImageURL string `db:"image_url" json:"image_url"` - - // We could use: sql.NullTime or unix time seconds (as int64), - // note that the dsn parameter "parseTime=true" is required now in order to fill this field correctly. - CreatedAt *time.Time `db:"created_at" json:"created_at"` - UpdatedAt *time.Time `db:"updated_at" json:"updated_at"` -} - -// TableName returns the database table name of a Category. -func (c *Category) TableName() string { - return "categories" -} - -// PrimaryKey returns the primary key of a Category. -func (c *Category) PrimaryKey() string { - return "id" -} - -// SortBy returns the column name that -// should be used as a fallback for sorting a set of Category. -func (c *Category) SortBy() string { - return "position" -} - -// Scan binds mysql rows to this Category. -func (c *Category) Scan(rows *sql.Rows) error { - c.CreatedAt = new(time.Time) - c.UpdatedAt = new(time.Time) - return rows.Scan(&c.ID, &c.Title, &c.Position, &c.ImageURL, &c.CreatedAt, &c.UpdatedAt) -} - -// Categories a list of categories. Implements the `Scannable` interface. -type Categories []*Category - -// Scan binds mysql rows to this Categories. -func (cs *Categories) Scan(rows *sql.Rows) (err error) { - cp := *cs - for rows.Next() { - c := new(Category) - if err = c.Scan(rows); err != nil { - return - } - cp = append(cp, c) - } - - if len(cp) == 0 { - return sql.ErrNoRows - } - - *cs = cp - - return rows.Err() -} - -/* -// The requests. -type ( - CreateCategoryRequest struct { - Title string `json:"title"` // all required. - Position uint64 `json:"position"` - ImageURL string `json:"imageURL"` - } - - UpdateCategoryRequest CreateCategoryRequest // at least 1 required. - - GetCategoryRequest struct { - ID int64 `json:"id"` // required. - } - - DeleteCategoryRequest GetCategoryRequest - - GetCategoriesRequest struct { - // [limit, offset...] - } -)*/ diff --git a/_examples/database/mysql/entity/product.go b/_examples/database/mysql/entity/product.go deleted file mode 100644 index bb352fbab2..0000000000 --- a/_examples/database/mysql/entity/product.go +++ /dev/null @@ -1,95 +0,0 @@ -package entity - -import ( - "database/sql" - "time" -) - -// Product represents the products entity. -// It implements the `sql.Record` and `sql.Sorted` interfaces. -type Product struct { - ID int64 `db:"id" json:"id"` - CategoryID int64 `db:"category_id" json:"category_id"` - Title string `db:"title" json:"title"` - ImageURL string `db:"image_url" json:"image_url"` - Price float32 `db:"price" json:"price"` - Description string `db:"description" json:"description"` - CreatedAt *time.Time `db:"created_at" json:"created_at"` - UpdatedAt *time.Time `db:"updated_at" json:"updated_at"` -} - -// TableName returns the database table name of a Product. -func (p Product) TableName() string { - return "products" -} - -// PrimaryKey returns the primary key of a Product. -func (p *Product) PrimaryKey() string { - return "id" -} - -// SortBy returns the column name that -// should be used as a fallback for sorting a set of Product. -func (p *Product) SortBy() string { - return "updated_at" -} - -// ValidateInsert simple check for empty fields that should be required. -func (p *Product) ValidateInsert() bool { - return p.CategoryID > 0 && p.Title != "" && p.ImageURL != "" && p.Price > 0 /* decimal* */ && p.Description != "" -} - -// Scan binds mysql rows to this Product. -func (p *Product) Scan(rows *sql.Rows) error { - p.CreatedAt = new(time.Time) - p.UpdatedAt = new(time.Time) - return rows.Scan(&p.ID, &p.CategoryID, &p.Title, &p.ImageURL, &p.Price, &p.Description, &p.CreatedAt, &p.UpdatedAt) -} - -// Products is a list of products. Implements the `Scannable` interface. -type Products []*Product - -// Scan binds mysql rows to this Categories. -func (ps *Products) Scan(rows *sql.Rows) (err error) { - cp := *ps - for rows.Next() { - p := new(Product) - if err = p.Scan(rows); err != nil { - return - } - cp = append(cp, p) - } - - if len(cp) == 0 { - return sql.ErrNoRows - } - - *ps = cp - - return rows.Err() -} - -/* -// The requests. -type ( - CreateProductRequest struct { // all required. - CategoryID int64 `json:"categoryID"` - Title string `json:"title"` - ImageURL string `json:"imageURL"` - Price float32 `json:"price"` - Description string `json:"description"` - } - - UpdateProductRequest CreateProductRequest // at least 1 required. - - GetProductRequest struct { - ID int64 `json:"id"` // required. - } - - DeleteProductRequest GetProductRequest - - GetProductsRequest struct { - // [page, offset...] - } -) -*/ diff --git a/_examples/database/mysql/go.mod b/_examples/database/mysql/go.mod deleted file mode 100644 index 3e7d3e7671..0000000000 --- a/_examples/database/mysql/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module myapp - -go 1.15 - -require ( - github.com/go-sql-driver/mysql v1.5.0 - github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd - github.com/mailgun/groupcache/v2 v2.1.0 -) diff --git a/_examples/database/mysql/main.go b/_examples/database/mysql/main.go deleted file mode 100644 index f7169c5d07..0000000000 --- a/_examples/database/mysql/main.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "fmt" - "log" - "os" - - "myapp/api" - "myapp/sql" - - "github.com/kataras/iris/v12" -) - -func main() { - dsn := fmt.Sprintf("%s:%s@tcp(%s:3306)/%s?parseTime=true&charset=utf8mb4&collation=utf8mb4_unicode_ci", - getenv("MYSQL_USER", "user_myapp"), - getenv("MYSQL_PASSWORD", "dbpassword"), - getenv("MYSQL_HOST", "localhost"), - getenv("MYSQL_DATABASE", "myapp"), - ) - - db, err := sql.ConnectMySQL(dsn) - if err != nil { - log.Fatalf("error connecting to the MySQL database: %v", err) - } - - secret := getenv("JWT_SECRET", "EbnJO3bwmX") - - app := iris.New() - subRouter := api.Router(db, secret) - app.PartyFunc("/", subRouter) - - addr := fmt.Sprintf(":%s", getenv("PORT", "8080")) - app.Listen(addr) -} - -func getenv(key string, def string) string { - v := os.Getenv(key) - if v == "" { - return def - } - - return v -} diff --git a/_examples/database/mysql/migration/api_category/create_category.json b/_examples/database/mysql/migration/api_category/create_category.json deleted file mode 100644 index 3ea1efa5d8..0000000000 --- a/_examples/database/mysql/migration/api_category/create_category.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "title": "computer-internet", - "position": 2, - "image_url": "https://bp.pstatic.gr/public/dist/images/1mOPxYtw1k.webp" -} \ No newline at end of file diff --git a/_examples/database/mysql/migration/api_category/insert_products_category.json b/_examples/database/mysql/migration/api_category/insert_products_category.json deleted file mode 100644 index 02051b7df3..0000000000 --- a/_examples/database/mysql/migration/api_category/insert_products_category.json +++ /dev/null @@ -1,31 +0,0 @@ -[{ - "title": "product-1", - "image_url": "https://images.product1.png", - "price": 42.42, - "description": "a description for product-1" -}, { - "title": "product-2", - "image_url": "https://images.product2.png", - "price": 32.1, - "description": "a description for product-2" -}, { - "title": "product-3", - "image_url": "https://images.product3.png", - "price": 52321321.32, - "description": "a description for product-3" -}, { - "title": "product-4", - "image_url": "https://images.product4.png", - "price": 77.4221, - "description": "a description for product-4" -}, { - "title": "product-5", - "image_url": "https://images.product5.png", - "price": 55.1, - "description": "a description for product-5" -}, { - "title": "product-6", - "image_url": "https://images.product6.png", - "price": 53.32, - "description": "a description for product-6" -}] \ No newline at end of file diff --git a/_examples/database/mysql/migration/api_category/update_category.json b/_examples/database/mysql/migration/api_category/update_category.json deleted file mode 100644 index a3ea1397ac..0000000000 --- a/_examples/database/mysql/migration/api_category/update_category.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "id": 2, - "position": 1, - "title": "computers", - "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Desktop_computer_clipart_-_Yellow_theme.svg/1200px-Desktop_computer_clipart_-_Yellow_theme.svg.png" -} \ No newline at end of file diff --git a/_examples/database/mysql/migration/api_category/update_partial_category.json b/_examples/database/mysql/migration/api_category/update_partial_category.json deleted file mode 100644 index 730c716655..0000000000 --- a/_examples/database/mysql/migration/api_category/update_partial_category.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "title": "computers-technology" -} \ No newline at end of file diff --git a/_examples/database/mysql/migration/api_postman.json b/_examples/database/mysql/migration/api_postman.json deleted file mode 100644 index 1ef1eba02d..0000000000 --- a/_examples/database/mysql/migration/api_postman.json +++ /dev/null @@ -1,484 +0,0 @@ -{ - "info": { - "_postman_id": "d3a2fdf6-9ebd-4e85-827d-385592a71fd6", - "name": "myapp (api-test)", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "Category", - "item": [ - { - "name": "Create", - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk2MzkzNjd9.cYohwgUpe-Z7ac0LPpz4Adi5QXJmtwD1ZRpXrMUMPN0", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\r\n \"title\": \"computer-internet\",\r\n \"position\": 1,\r\n \"image_url\": \"https://bp.pstatic.gr/public/dist/images/1mOPxYtw1k.webp\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8080/category", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "category" - ] - }, - "description": "Create a Category" - }, - "response": [] - }, - { - "name": "Get By ID", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "http://localhost:8080/category/1", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "category", - "1" - ] - }, - "description": "Get By ID" - }, - "response": [] - }, - { - "name": "List", - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "http://localhost:8080/category?offset=0&limit=30&order=asc", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "category" - ], - "query": [ - { - "key": "offset", - "value": "0" - }, - { - "key": "limit", - "value": "30" - }, - { - "key": "order", - "value": "asc" - } - ] - }, - "description": "Get many with limit offset" - }, - "response": [] - }, - { - "name": "Update (Full)", - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\r\n\t\"id\": 1,\r\n\t\"position\": 3,\r\n \"title\": \"computers\",\r\n \"image_url\":\"https://upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Desktop_computer_clipart_-_Yellow_theme.svg/1200px-Desktop_computer_clipart_-_Yellow_theme.svg.png\"\r\n}" - }, - "url": { - "raw": "http://localhost:8080/category", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "category" - ], - "query": [ - { - "key": "", - "value": null, - "disabled": true - } - ] - }, - "description": "Update a Category (full update)" - }, - "response": [] - }, - { - "name": "Delete By ID", - "request": { - "method": "DELETE", - "header": [ - { - "key": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU", - "type": "text" - } - ], - "url": { - "raw": "http://localhost:8080/category/1", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "category", - "1" - ] - }, - "description": "Delete a Category" - }, - "response": [] - }, - { - "name": "Update (Partial)", - "request": { - "method": "PATCH", - "header": [ - { - "key": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\r\n \"title\": \"computers-technology\"\r\n}" - }, - "url": { - "raw": "http://localhost:8080/category/1", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "category", - "3" - ] - }, - "description": "Update a Category partially, e.g. title only" - }, - "response": [] - }, - { - "name": "List Products", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "http://localhost:8080/category/1/products?offset=0&limit=30&by=price&order=asc", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "category", - "3", - "products" - ], - "query": [ - { - "key": "offset", - "value": "0" - }, - { - "key": "limit", - "value": "30" - }, - { - "key": "by", - "value": "price" - }, - { - "key": "order", - "value": "asc" - } - ] - }, - "description": "Get products from cheap to expensive" - }, - "response": [] - }, - { - "name": "Insert Products", - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "[{\r\n \"title\": \"product-1\",\r\n \"image_url\": \"https://images.product1.png\",\r\n \"price\": 42.42,\r\n \"description\": \"a description for product-1\"\r\n}, {\r\n \"title\": \"product-2\",\r\n \"image_url\": \"https://images.product2.png\",\r\n \"price\": 32.1,\r\n \"description\": \"a description for product-2\"\r\n}, {\r\n \"title\": \"product-3\",\r\n \"image_url\": \"https://images.product3.png\",\r\n \"price\": 52321321.32,\r\n \"description\": \"a description for product-3\"\r\n}, {\r\n \"title\": \"product-4\",\r\n \"image_url\": \"https://images.product4.png\",\r\n \"price\": 77.4221,\r\n \"description\": \"a description for product-4\"\r\n}, {\r\n \"title\": \"product-5\",\r\n \"image_url\": \"https://images.product5.png\",\r\n \"price\": 55.1,\r\n \"description\": \"a description for product-5\"\r\n}, {\r\n \"title\": \"product-6\",\r\n \"image_url\": \"https://images.product6.png\",\r\n \"price\": 53.32,\r\n \"description\": \"a description for product-6\"\r\n}]" - }, - "url": { - "raw": "http://localhost:8080/category/1/products", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "category", - "3", - "products" - ] - }, - "description": "Batch Insert Products to a Category" - }, - "response": [] - } - ], - "protocolProfileBehavior": {} - }, - { - "name": "Product", - "item": [ - { - "name": "List", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "http://localhost:8080/product?offset=0&limit=30&by=price&order=asc", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "product" - ], - "query": [ - { - "key": "offset", - "value": "0" - }, - { - "key": "limit", - "value": "30" - }, - { - "key": "by", - "value": "price" - }, - { - "key": "order", - "value": "asc" - } - ] - }, - "description": "List products" - }, - "response": [] - }, - { - "name": "Get By ID", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "http://localhost:8080/product/1", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "product", - "1" - ] - }, - "description": "Get a Product" - }, - "response": [] - }, - { - "name": "Delete By ID", - "request": { - "method": "DELETE", - "header": [ - { - "key": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU", - "type": "text" - } - ], - "url": { - "raw": "http://localhost:8080/product/3", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "product", - "3" - ] - }, - "description": "Delete a Product" - }, - "response": [] - }, - { - "name": "Create", - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\r\n \"title\": \"product-1\",\r\n \"category_id\": 1,\r\n \"image_url\": \"https://images.product1.png\",\r\n \"price\": 42.42,\r\n \"description\": \"a description for product-1\"\r\n}" - }, - "url": { - "raw": "http://localhost:8080/product", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "product" - ] - }, - "description": "Create a Product (and assign a category)" - }, - "response": [] - }, - { - "name": "Update (Full)", - "request": { - "method": "PUT", - "header": [ - { - "key": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\r\n\t\"id\":19,\r\n \"title\": \"product-9-new\",\r\n \"category_id\": 1,\r\n \"image_url\": \"https://images.product19.png\",\r\n \"price\": 20,\r\n \"description\": \"a description for product-9-new\"\r\n}" - }, - "url": { - "raw": "http://localhost:8080/product", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "product" - ] - }, - "description": "Update a Product (full-update)" - }, - "response": [] - }, - { - "name": "Update (Partial)", - "request": { - "method": "PATCH", - "header": [ - { - "key": "Authorization", - "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODk1ODU1NjN9.PtfDS1niGoZ7pV6kplI-_q1fVKLnknQ3IwcrLZhoVCU", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\r\n \"title\": \"product-9-new-title\"\r\n}" - }, - "url": { - "raw": "http://localhost:8080/product/9", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "product", - "9" - ] - }, - "description": "Update a Product (partially)" - }, - "response": [] - } - ], - "description": "Product Client API", - "protocolProfileBehavior": {} - }, - { - "name": "Get Token", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "http://localhost:8080/token", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "token" - ] - }, - "description": "Get Token to access \"write\" (create, update and delete) endpoints" - }, - "response": [] - } - ], - "protocolProfileBehavior": {} -} \ No newline at end of file diff --git a/_examples/database/mysql/migration/api_product/create_product.json b/_examples/database/mysql/migration/api_product/create_product.json deleted file mode 100644 index 46b6a5c897..0000000000 --- a/_examples/database/mysql/migration/api_product/create_product.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "title": "product-1", - "category_id": 3, - "image_url": "https://images.product1.png", - "price": 42.42, - "description": "a description for product-1" -} \ No newline at end of file diff --git a/_examples/database/mysql/migration/api_product/update_partial_product.json b/_examples/database/mysql/migration/api_product/update_partial_product.json deleted file mode 100644 index 71413c1305..0000000000 --- a/_examples/database/mysql/migration/api_product/update_partial_product.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "title": "product-19-new-title" -} \ No newline at end of file diff --git a/_examples/database/mysql/migration/api_product/update_product.json b/_examples/database/mysql/migration/api_product/update_product.json deleted file mode 100644 index d8e526626f..0000000000 --- a/_examples/database/mysql/migration/api_product/update_product.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "id":19, - "title": "product-19", - "category_id": 3, - "image_url": "https://images.product19.png", - "price": 20, - "description": "a description for product-19" -} \ No newline at end of file diff --git a/_examples/database/mysql/migration/db.sql b/_examples/database/mysql/migration/db.sql deleted file mode 100644 index eab35314a8..0000000000 --- a/_examples/database/mysql/migration/db.sql +++ /dev/null @@ -1,33 +0,0 @@ -CREATE DATABASE IF NOT EXISTS myapp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - -USE myapp; - -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; - -DROP TABLE IF EXISTS categories; -CREATE TABLE categories ( - id int(11) NOT NULL AUTO_INCREMENT, - title varchar(255) NOT NULL, - position int(11) NOT NULL, - image_url varchar(255) NOT NULL, - created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (id) -); - -DROP TABLE IF EXISTS products; -CREATE TABLE products ( - id int(11) NOT NULL AUTO_INCREMENT, - category_id int, - title varchar(255) NOT NULL, - image_url varchar(255) NOT NULL, - price decimal(10,2) NOT NULL, - description text NOT NULL, - created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (id), - FOREIGN KEY (category_id) REFERENCES categories(id) -); - -SET FOREIGN_KEY_CHECKS = 1; \ No newline at end of file diff --git a/_examples/database/mysql/service/category_service.go b/_examples/database/mysql/service/category_service.go deleted file mode 100644 index 2a151d8324..0000000000 --- a/_examples/database/mysql/service/category_service.go +++ /dev/null @@ -1,74 +0,0 @@ -package service - -import ( - "context" - "fmt" - "reflect" - - "myapp/entity" - "myapp/sql" -) - -// CategoryService represents the category entity service. -// Note that the given entity (request) should be already validated -// before service's calls. -type CategoryService struct { - *sql.Service -} - -// NewCategoryService returns a new category service to communicate with the database. -func NewCategoryService(db sql.Database) *CategoryService { - return &CategoryService{Service: sql.NewService(db, new(entity.Category))} -} - -// Insert stores a category to the database and returns its ID. -func (s *CategoryService) Insert(ctx context.Context, e entity.Category) (int64, error) { - if e.Title == "" || e.ImageURL == "" { - return 0, sql.ErrUnprocessable - } - - q := fmt.Sprintf(`INSERT INTO %s (title, position, image_url) - VALUES (?,?,?);`, e.TableName()) - - res, err := s.DB().Exec(ctx, q, e.Title, e.Position, e.ImageURL) - if err != nil { - return 0, err - } - - return res.LastInsertId() -} - -// Update updates a category based on its `ID`. -func (s *CategoryService) Update(ctx context.Context, e entity.Category) (int, error) { - if e.ID == 0 || e.Title == "" || e.ImageURL == "" { - return 0, sql.ErrUnprocessable - } - - q := fmt.Sprintf(`UPDATE %s - SET - title = ?, - position = ?, - image_url = ? - WHERE %s = ?;`, e.TableName(), e.PrimaryKey()) - - res, err := s.DB().Exec(ctx, q, e.Title, e.Position, e.ImageURL, e.ID) - if err != nil { - return 0, err - } - - n := sql.GetAffectedRows(res) - return n, nil -} - -// The updatable fields, separately from that we create for any possible future necessities. -var categoryUpdateSchema = map[string]reflect.Kind{ - "title": reflect.String, - "image_url": reflect.String, - "position": reflect.Int, -} - -// PartialUpdate accepts a key-value map to -// update the record based on the given "id". -func (s *CategoryService) PartialUpdate(ctx context.Context, id int64, attrs map[string]interface{}) (int, error) { - return s.Service.PartialUpdate(ctx, id, categoryUpdateSchema, attrs) -} diff --git a/_examples/database/mysql/service/category_service_test.go b/_examples/database/mysql/service/category_service_test.go deleted file mode 100644 index 24c6ef8651..0000000000 --- a/_examples/database/mysql/service/category_service_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package service - -import ( - "context" - "testing" - - "myapp/entity" - "myapp/sql" - - "github.com/DATA-DOG/go-sqlmock" -) - -func TestCategoryServiceInsert(t *testing.T) { - conn, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) - if err != nil { - t.Fatal(err) - } - defer conn.Close() - - db := &sql.MySQL{Conn: conn} - service := NewCategoryService(db) - newCategory := entity.Category{ - Title: "computer-internet", - Position: 2, - ImageURL: "https://animage", - } - mock.ExpectExec("INSERT INTO categories (title, position, image_url) VALUES (?,?,?);"). - WithArgs(newCategory.Title, newCategory.Position, newCategory.ImageURL).WillReturnResult(sqlmock.NewResult(1, 1)) - - id, err := service.Insert(context.TODO(), newCategory) - if err != nil { - t.Fatal(err) - } - - if id != 1 { - t.Fatalf("expected ID to be 1 as this is the first entry") - } - - if err = mock.ExpectationsWereMet(); err != nil { - t.Fatal(err) - } -} diff --git a/_examples/database/mysql/service/product_service.go b/_examples/database/mysql/service/product_service.go deleted file mode 100644 index 15d7bfdc07..0000000000 --- a/_examples/database/mysql/service/product_service.go +++ /dev/null @@ -1,110 +0,0 @@ -package service - -import ( - "context" - "fmt" - "reflect" - "strings" - - "myapp/entity" - "myapp/sql" -) - -// ProductService represents the product entity service. -// Note that the given entity (request) should be already validated -// before service's calls. -type ProductService struct { - *sql.Service - rec sql.Record -} - -// NewProductService returns a new product service to communicate with the database. -func NewProductService(db sql.Database) *ProductService { - return &ProductService{Service: sql.NewService(db, new(entity.Product))} -} - -// Insert stores a product to the database and returns its ID. -func (s *ProductService) Insert(ctx context.Context, e entity.Product) (int64, error) { - if !e.ValidateInsert() { - return 0, sql.ErrUnprocessable - } - - q := fmt.Sprintf(`INSERT INTO %s (category_id, title, image_url, price, description) - VALUES (?,?,?,?,?);`, e.TableName()) - - res, err := s.DB().Exec(ctx, q, e.CategoryID, e.Title, e.ImageURL, e.Price, e.Description) - if err != nil { - return 0, err - } - - return res.LastInsertId() -} - -// BatchInsert inserts one or more products at once and returns the total length created. -func (s *ProductService) BatchInsert(ctx context.Context, products []entity.Product) (int, error) { - if len(products) == 0 { - return 0, nil - } - - var ( - valuesLines []string - args []interface{} - ) - - for _, p := range products { - if !p.ValidateInsert() { - // all products should be "valid", we don't skip, we cancel. - return 0, sql.ErrUnprocessable - } - - valuesLines = append(valuesLines, "(?,?,?,?,?)") - args = append(args, []interface{}{p.CategoryID, p.Title, p.ImageURL, p.Price, p.Description}...) - } - - q := fmt.Sprintf("INSERT INTO %s (category_id, title, image_url, price, description) VALUES %s;", - s.RecordInfo().TableName(), - strings.Join(valuesLines, ", ")) - - res, err := s.DB().Exec(ctx, q, args...) - if err != nil { - return 0, err - } - - n := sql.GetAffectedRows(res) - return n, nil -} - -// Update updates a product based on its `ID` from the database -// and returns the affected numbrer (0 when nothing changed otherwise 1). -func (s *ProductService) Update(ctx context.Context, e entity.Product) (int, error) { - q := fmt.Sprintf(`UPDATE %s - SET - category_id = ?, - title = ?, - image_url = ?, - price = ?, - description = ? - WHERE %s = ?;`, e.TableName(), e.PrimaryKey()) - - res, err := s.DB().Exec(ctx, q, e.CategoryID, e.Title, e.ImageURL, e.Price, e.Description, e.ID) - if err != nil { - return 0, err - } - - n := sql.GetAffectedRows(res) - return n, nil -} - -var productUpdateSchema = map[string]reflect.Kind{ - "category_id": reflect.Int, - "title": reflect.String, - "image_url": reflect.String, - "price": reflect.Float32, - "description": reflect.String, -} - -// PartialUpdate accepts a key-value map to -// update the record based on the given "id". -func (s *ProductService) PartialUpdate(ctx context.Context, id int64, attrs map[string]interface{}) (int, error) { - return s.Service.PartialUpdate(ctx, id, productUpdateSchema, attrs) -} diff --git a/_examples/database/mysql/sql/mysql.go b/_examples/database/mysql/sql/mysql.go deleted file mode 100644 index 9d93b5cde4..0000000000 --- a/_examples/database/mysql/sql/mysql.go +++ /dev/null @@ -1,123 +0,0 @@ -package sql - -import ( - "context" - "database/sql" - "fmt" - - _ "github.com/go-sql-driver/mysql" // lint: mysql driver. -) - -// MySQL holds the underline connection of a MySQL (or MariaDB) database. -// See the `ConnectMySQL` package-level function. -type MySQL struct { - Conn *sql.DB -} - -var _ Database = (*MySQL)(nil) - -var ( - // DefaultCharset default charset parameter for new databases. - DefaultCharset = "utf8mb4" - // DefaultCollation default collation parameter for new databases. - DefaultCollation = "utf8mb4_unicode_ci" -) - -// ConnectMySQL returns a new ready to use MySQL Database instance. -// Accepts a single argument of "dsn", i.e: -// username:password@tcp(localhost:3306)/myapp?parseTime=true&charset=utf8mb4&collation=utf8mb4_unicode_ci -func ConnectMySQL(dsn string) (*MySQL, error) { - conn, err := sql.Open("mysql", dsn) - if err != nil { - return nil, err - } - err = conn.Ping() - if err != nil { - conn.Close() - return nil, err - } - - return &MySQL{ - Conn: conn, - }, nil -} - -// CreateDatabase executes the CREATE DATABASE query. -func (db *MySQL) CreateDatabase(database string) error { - q := fmt.Sprintf("CREATE DATABASE %s DEFAULT CHARSET = %s COLLATE = %s;", database, DefaultCharset, DefaultCollation) - _, err := db.Conn.Exec(q) - return err -} - -// Drop executes the DROP DATABASE query. -func (db *MySQL) Drop(database string) error { - q := fmt.Sprintf("DROP DATABASE %s;", database) - _, err := db.Conn.Exec(q) - return err -} - -// Select performs the SELECT query for this database (dsn database name is required). -func (db *MySQL) Select(ctx context.Context, dest interface{}, query string, args ...interface{}) error { - rows, err := db.Conn.QueryContext(ctx, query, args...) - if err != nil { - return err - } - defer rows.Close() - - if scannable, ok := dest.(Scannable); ok { - return scannable.Scan(rows) - } - - if !rows.Next() { - return ErrNoRows - } - return rows.Scan(dest) - - /* Uncomment this and pass a slice if u want to see reflection powers <3 - v, ok := dest.(reflect.Value) - if !ok { - v = reflect.Indirect(reflect.ValueOf(dest)) - } - - sliceTyp := v.Type() - - if sliceTyp.Kind() != reflect.Slice { - sliceTyp = reflect.SliceOf(sliceTyp) - } - - sliceElementTyp := deref(sliceTyp.Elem()) - for rows.Next() { - obj := reflect.New(sliceElementTyp) - obj.Interface().(Scannable).Scan(rows) - if err != nil { - return err - } - - v.Set(reflect.Append(v, reflect.Indirect(obj))) - } - */ -} - -// Get same as `Select` but it moves the cursor to the first result. -func (db *MySQL) Get(ctx context.Context, dest interface{}, query string, args ...interface{}) error { - rows, err := db.Conn.QueryContext(ctx, query, args...) - if err != nil { - return err - } - defer rows.Close() - if !rows.Next() { - return ErrNoRows - } - - if scannable, ok := dest.(Scannable); ok { - return scannable.Scan(rows) - } - - return rows.Scan(dest) -} - -// Exec executes a query. It does not return any rows. -// Use the first output parameter to count the affected rows on UPDATE, INSERT, or DELETE. -func (db *MySQL) Exec(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { - return db.Conn.ExecContext(ctx, query, args...) -} diff --git a/_examples/database/mysql/sql/service.go b/_examples/database/mysql/sql/service.go deleted file mode 100644 index 153a2e6b4c..0000000000 --- a/_examples/database/mysql/sql/service.go +++ /dev/null @@ -1,243 +0,0 @@ -package sql - -import ( - "context" - "database/sql" - "errors" - "fmt" - "net/url" - "reflect" - "strconv" - "strings" -) - -// Service holder for common queries. -// Note: each entity service keeps its own base Service instance. -type Service struct { - db Database - rec Record // see `Count`, `List` and `DeleteByID` methods. -} - -// NewService returns a new (SQL) base service for common operations. -func NewService(db Database, of Record) *Service { - return &Service{db: db, rec: of} -} - -// DB exposes the database instance. -func (s *Service) DB() Database { - return s.db -} - -// RecordInfo returns the record info provided through `NewService`. -func (s *Service) RecordInfo() Record { - return s.rec -} - -// ErrNoRows is returned when GET doesn't return a row. -// A shortcut of sql.ErrNoRows. -var ErrNoRows = sql.ErrNoRows - -// GetByID binds a single record from the databases to the "dest". -func (s *Service) GetByID(ctx context.Context, dest interface{}, id int64) error { - q := fmt.Sprintf("SELECT * FROM %s WHERE %s = ? LIMIT 1", s.rec.TableName(), s.rec.PrimaryKey()) - err := s.db.Get(ctx, dest, q, id) - return err - // if err != nil { - // if err == sql.ErrNoRows { - // return false, nil - // } - - // return false, err - // } - - // return true, nil -} - -// Count returns the total records count in the table. -func (s *Service) Count(ctx context.Context) (total int64, err error) { - q := fmt.Sprintf("SELECT COUNT(DISTINCT %s) FROM %s", s.rec.PrimaryKey(), s.rec.TableName()) - if err = s.db.Select(ctx, &total, q); err == sql.ErrNoRows { - err = nil - } - return -} - -// ListOptions holds the options to be passed on the `Service.List` method. -type ListOptions struct { - Table string // the table name. - Offset uint64 // inclusive. - Limit uint64 - OrderByColumn string - Order string // "ASC" or "DESC" (could be a bool type instead). - WhereColumn string - WhereValue interface{} -} - -// Where accepts a column name and column value to set -// on the WHERE clause of the result query. -// It returns a new `ListOptions` value. -// Note that this is a basic implementation which just takes care our current needs. -func (opt ListOptions) Where(colName string, colValue interface{}) ListOptions { - opt.WhereColumn = colName - opt.WhereValue = colValue - return opt -} - -// BuildQuery returns the query and the arguments that -// should be form a SELECT command. -func (opt ListOptions) BuildQuery() (q string, args []interface{}) { - q = fmt.Sprintf("SELECT * FROM %s", opt.Table) - - if opt.WhereColumn != "" && opt.WhereValue != nil { - q += fmt.Sprintf(" WHERE %s = ?", opt.WhereColumn) - args = append(args, opt.WhereValue) - } - - if opt.OrderByColumn != "" { - q += fmt.Sprintf(" ORDER BY %s %s", opt.OrderByColumn, ParseOrder(opt.Order)) - } - - if opt.Limit > 0 { - q += fmt.Sprintf(" LIMIT %d", opt.Limit) // offset below. - } - - if opt.Offset > 0 { - q += fmt.Sprintf(" OFFSET %d", opt.Offset) - } - - return -} - -// const defaultLimit = 30 // default limit if not set. - -// ParseListOptions returns a `ListOptions` from a map[string][]string. -func ParseListOptions(q url.Values) ListOptions { - offset, _ := strconv.ParseUint(q.Get("offset"), 10, 64) - limit, _ := strconv.ParseUint(q.Get("limit"), 10, 64) - order := q.Get("order") // empty, asc(...) or desc(...). - orderBy := q.Get("by") // e.g. price - - return ListOptions{Offset: offset, Limit: limit, Order: order, OrderByColumn: orderBy} -} - -// List binds one or more records from the database to the "dest". -// If the record supports ordering then it will sort by the `Sorted.OrderBy` column name(s). -// Use the "order" input parameter to set a descending order ("DESC"). -func (s *Service) List(ctx context.Context, dest interface{}, opts ListOptions) error { - // Set table and order by column from record info for `List` by options - // so it can be more flexible to perform read-only calls of other table's too. - if opts.Table == "" { - // If missing then try to set it by record info. - opts.Table = s.rec.TableName() - } - if opts.OrderByColumn == "" { - if b, ok := s.rec.(Sorted); ok { - opts.OrderByColumn = b.SortBy() - } - } - - q, args := opts.BuildQuery() - return s.db.Select(ctx, dest, q, args...) -} - -// DeleteByID removes a single record of "dest" from the database. -func (s *Service) DeleteByID(ctx context.Context, id int64) (int, error) { - q := fmt.Sprintf("DELETE FROM %s WHERE %s = ? LIMIT 1", s.rec.TableName(), s.rec.PrimaryKey()) - res, err := s.db.Exec(ctx, q, id) - if err != nil { - return 0, err - } - - return GetAffectedRows(res), nil -} - -// ErrUnprocessable indicates error caused by invalid entity (entity's key-values). -// The syntax of the request entity is correct, but it was unable to process the contained instructions -// e.g. empty or unsupported value. -// -// See `../service/XService.Insert` and `../service/XService.Update` -// and `PartialUpdate`. -var ErrUnprocessable = errors.New("invalid entity") - -// PartialUpdate accepts a columns schema and a key-value map to -// update the record based on the given "id". -// Note: Trivial string, int and boolean type validations are performed here. -func (s *Service) PartialUpdate(ctx context.Context, id int64, schema map[string]reflect.Kind, attrs map[string]interface{}) (int, error) { - if len(schema) == 0 || len(attrs) == 0 { - return 0, nil - } - - var ( - keyLines []string - values []interface{} - ) - - for key, kind := range schema { - v, ok := attrs[key] - if !ok { - continue - } - - switch v.(type) { - case string: - if kind != reflect.String { - return 0, ErrUnprocessable - } - case int: - if kind != reflect.Int { - return 0, ErrUnprocessable - } - case bool: - if kind != reflect.Bool { - return 0, ErrUnprocessable - } - } - - keyLines = append(keyLines, fmt.Sprintf("%s = ?", key)) - values = append(values, v) - } - - if len(values) == 0 { - return 0, nil - } - - q := fmt.Sprintf("UPDATE %s SET %s WHERE %s = ?;", - s.rec.TableName(), strings.Join(keyLines, ", "), s.rec.PrimaryKey()) - - res, err := s.DB().Exec(ctx, q, append(values, id)...) - if err != nil { - return 0, err - } - - n := GetAffectedRows(res) - return n, nil -} - -// GetAffectedRows returns the number of affected rows after -// a DELETE or UPDATE operation. -func GetAffectedRows(result sql.Result) int { - if result == nil { - return 0 - } - - n, _ := result.RowsAffected() - return int(n) -} - -const ( - ascending = "ASC" - descending = "DESC" -) - -// ParseOrder accept an order string and returns a valid mysql ORDER clause. -// Defaults to "ASC". Two possible outputs: "ASC" and "DESC". -func ParseOrder(order string) string { - order = strings.TrimSpace(order) - if len(order) >= 4 { - if strings.HasPrefix(strings.ToUpper(order), descending) { - return descending - } - } - - return ascending -} diff --git a/_examples/database/mysql/sql/sql.go b/_examples/database/mysql/sql/sql.go deleted file mode 100644 index 4ac6ec9cdf..0000000000 --- a/_examples/database/mysql/sql/sql.go +++ /dev/null @@ -1,40 +0,0 @@ -package sql - -import ( - "context" - "database/sql" -) - -// Database is an interface which a database(sql) should implement. -type Database interface { - Get(ctx context.Context, dest interface{}, q string, args ...interface{}) error - Select(ctx context.Context, dest interface{}, q string, args ...interface{}) error - Exec(ctx context.Context, q string, args ...interface{}) (sql.Result, error) -} - -// Record should represent a database record. -// It holds the table name and the primary key. -// Entities should implement that -// in order to use the BaseService's methods. -type Record interface { - TableName() string // the table name which record belongs to. - PrimaryKey() string // the primary key of the record. -} - -// Sorted should represent a set of database records -// that should be rendered with order. -// -// It does NOT support the form of -// column1 ASC, -// column2 DESC -// The OrderBy method should return text in form of: -// column1 -// or column1, column2 -type Sorted interface { - SortBy() string // column names separated by comma. -} - -// Scannable for go structs to bind their fields. -type Scannable interface { - Scan(*sql.Rows) error -} diff --git a/_examples/database/orm/gorm/REAMDE.md b/_examples/database/orm/gorm/REAMDE.md deleted file mode 100644 index 7e684d63c9..0000000000 --- a/_examples/database/orm/gorm/REAMDE.md +++ /dev/null @@ -1,5 +0,0 @@ -# GORM - -This example is pull by [#1275 PR](https://github.com/kataras/iris/pull/1275) by [@wuxiaoxiaoshen](https://github.com/wuxiaoxiaoshen). - -A more complete and real-world example can be found at the project created by [@snowlyg](https://github.com/snowlyg). diff --git a/_examples/database/orm/gorm/main.go b/_examples/database/orm/gorm/main.go deleted file mode 100644 index 00a10b3c45..0000000000 --- a/_examples/database/orm/gorm/main.go +++ /dev/null @@ -1,177 +0,0 @@ -package main - -import ( - "net/http" - "os" - "time" - - "github.com/jinzhu/gorm" - "github.com/kataras/iris/v12" - _ "github.com/mattn/go-sqlite3" -) - -type User struct { - gorm.Model - Salt string `gorm:"type:varchar(255)" json:"salt"` - Username string `gorm:"type:varchar(32)" json:"username"` - Password string `gorm:"type:varchar(200);column:password" json:"-"` - Languages string `gorm:"type:varchar(200);column:languages" json:"languages"` -} - -func (u User) TableName() string { - return "gorm_user" -} - -type UserSerializer struct { - ID uint `json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Salt string `json:"salt"` - UserName string `json:"user_name"` - Password string `json:"-"` - Languages string `json:"languages"` -} - -func (self User) Serializer() UserSerializer { - return UserSerializer{ - ID: self.ID, - CreatedAt: self.CreatedAt.Truncate(time.Second), - UpdatedAt: self.UpdatedAt.Truncate(time.Second), - Salt: self.Salt, - Password: self.Password, - Languages: self.Languages, - UserName: self.Username, - } -} - -func main() { - app := iris.Default() - db, err := gorm.Open("sqlite3", "test.db") - db.LogMode(true) // show SQL logger - if err != nil { - app.Logger().Fatalf("connect to sqlite3 failed") - return - } - iris.RegisterOnInterrupt(func() { - defer db.Close() - }) - - if os.Getenv("ENV") != "" { - db.DropTableIfExists(&User{}) // drop table - } - db.AutoMigrate(&User{}) // create table: // AutoMigrate run auto migration for given models, will only add missing fields, won't delete/change current data - - app.Post("/post_user", func(ctx iris.Context) { - var user User - user = User{ - Username: "gorm", - Salt: "hash---", - Password: "admin", - Languages: "gorm", - } - if err := db.FirstOrCreate(&user); err == nil { - app.Logger().Fatalf("created one record failed: %s", err.Error) - ctx.JSON(iris.Map{ - "code": http.StatusBadRequest, - "error": err.Error, - }) - return - } - ctx.JSON( - iris.Map{ - "code": http.StatusOK, - "data": user.Serializer(), - }) - }) - - app.Get("/get_user/{id:uint}", func(ctx iris.Context) { - var user User - id, _ := ctx.Params().GetUint("id") - app.Logger().Println(id) - if err := db.Where("id = ?", int(id)).First(&user).Error; err != nil { - app.Logger().Fatalf("find one record failed: %t", err == nil) - ctx.JSON(iris.Map{ - "code": http.StatusBadRequest, - "error": err.Error, - }) - return - } - ctx.JSON(iris.Map{ - "code": http.StatusOK, - "data": user.Serializer(), - }) - }) - - app.Delete("/delete_user/{id:uint}", func(ctx iris.Context) { - id, _ := ctx.Params().GetUint("id") - if id == 0 { - ctx.JSON(iris.Map{ - "code": http.StatusOK, - "detail": "query param id should not be nil", - }) - return - } - var user User - if err := db.Where("id = ?", id).First(&user).Error; err != nil { - app.Logger().Fatalf("record not found") - ctx.JSON(iris.Map{ - "code": http.StatusOK, - "detail": err.Error, - }) - return - } - db.Delete(&user) - ctx.JSON(iris.Map{ - "code": http.StatusOK, - "data": user.Serializer(), - }) - }) - - app.Patch("/patch_user/{id:uint}", func(ctx iris.Context) { - id, _ := ctx.Params().GetUint("id") - if id == 0 { - ctx.JSON(iris.Map{ - "code": http.StatusOK, - "detail": "query param id should not be nil", - }) - return - } - var user User - tx := db.Begin() - if err := tx.Where("id = ?", id).First(&user).Error; err != nil { - app.Logger().Fatalf("record not found") - ctx.JSON(iris.Map{ - "code": http.StatusOK, - "detail": err.Error, - }) - return - } - - var body patchParam - ctx.ReadJSON(&body) - app.Logger().Println(body) - if err := tx.Model(&user).Updates(map[string]interface{}{"username": body.Data.UserName, "password": body.Data.Password}).Error; err != nil { - app.Logger().Fatalf("update record failed") - tx.Rollback() - ctx.JSON(iris.Map{ - "code": http.StatusBadRequest, - "error": err.Error, - }) - return - } - tx.Commit() - ctx.JSON(iris.Map{ - "code": http.StatusOK, - "data": user.Serializer(), - }) - }) - - app.Listen(":8080") -} - -type patchParam struct { - Data struct { - UserName string `json:"user_name" form:"user_name"` - Password string `json:"password" form:"password"` - } `json:"data"` -} diff --git a/_examples/database/orm/reform/controllers/person_controller.go b/_examples/database/orm/reform/controllers/person_controller.go deleted file mode 100644 index 97f9dbb6f0..0000000000 --- a/_examples/database/orm/reform/controllers/person_controller.go +++ /dev/null @@ -1,47 +0,0 @@ -package controllers - -import ( - "net" - "time" - - "myapp/models" - - "github.com/kataras/golog" - "gopkg.in/reform.v1" -) - -// PersonController is the model.Person's web controller. -type PersonController struct { - DB *reform.DB - - // Logger and IP fields are automatically binded by the framework. - Logger *golog.Logger // binds to the application's logger. - IP net.IP // binds to the client's IP. -} - -// Get handles -// GET /persons -func (c *PersonController) Get() ([]reform.Struct, error) { - return c.DB.SelectAllFrom(models.PersonTable, "") -} - -// GetBy handles -// GET /persons/{ID} -func (c *PersonController) GetBy(id int32) (reform.Record, error) { - return c.DB.FindByPrimaryKeyFrom(models.PersonTable, id) -} - -// Post handles -// POST /persons with JSON request body of model.Person. -func (c *PersonController) Post(p *models.Person) int { - p.CreatedAt = time.Now() - - if err := c.DB.Save(p); err != nil { - c.Logger.Errorf("[%s] create person: %v", c.IP.String(), err) - return 500 // iris.StatusInternalServerError - } - - c.Logger.Debugf("[%s] create person [%s] succeed", c.IP.String(), p.Name) - - return 201 // iris.StatusCreated -} diff --git a/_examples/database/orm/reform/go.mod b/_examples/database/orm/reform/go.mod deleted file mode 100644 index 039ec25ac2..0000000000 --- a/_examples/database/orm/reform/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module myapp - -go 1.15 - -require ( - github.com/kataras/iris/v12 v12.1.8 - github.com/mattn/go-sqlite3 v1.14.0 - gopkg.in/reform.v1 v1.4.0 -) - -replace github.com/kataras/iris/v12 => ../../../../ diff --git a/_examples/database/orm/reform/main.go b/_examples/database/orm/reform/main.go deleted file mode 100644 index 9be385b410..0000000000 --- a/_examples/database/orm/reform/main.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -/* -$ go get gopkg.in/reform.v1/reform -$ go generate ./models -$ go run . - -Read more at: https://github.com/go-reform/reform -*/ - -import ( - "database/sql" - - "myapp/controllers" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" - - _ "github.com/mattn/go-sqlite3" - "gopkg.in/reform.v1" - "gopkg.in/reform.v1/dialects/sqlite3" -) - -func main() { - app := iris.New() - app.Logger().SetLevel("debug") - - sqlDB, err := sql.Open("sqlite3", "./myapp.db") - if err != nil { - panic(err) - } - defer sqlDB.Close() - sqlStmt := ` - drop table people; - create table people (id integer not null primary key, name text, email text, created_at datetime not null, updated_at datetime null); - delete from people; - ` - _, err = sqlDB.Exec(sqlStmt) - if err != nil { - panic(err) - } - - db := reform.NewDB(sqlDB, sqlite3.Dialect, reform.NewPrintfLogger(app.Logger().Debugf)) - - mvcApp := mvc.New(app.Party("/persons")) - mvcApp.Register(db) - mvcApp.Handle(new(controllers.PersonController)) - - app.Listen(":8080") -} diff --git a/_examples/database/orm/reform/models/person.go b/_examples/database/orm/reform/models/person.go deleted file mode 100644 index 6b852748ed..0000000000 --- a/_examples/database/orm/reform/models/person.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:generate reform -package models - -import "time" - -//reform:people -type Person struct { - ID int32 `reform:"id,pk" json:"id"` - Name string `reform:"name" json:"name"` - Email *string `reform:"email" json:"email"` - CreatedAt time.Time `reform:"created_at" json:"created_at"` - UpdatedAt *time.Time `reform:"updated_at" json:"updated_at"` -} diff --git a/_examples/database/orm/reform/models/person_reform.go b/_examples/database/orm/reform/models/person_reform.go deleted file mode 100644 index 537d2b5d21..0000000000 --- a/_examples/database/orm/reform/models/person_reform.go +++ /dev/null @@ -1,136 +0,0 @@ -// Code generated by gopkg.in/reform.v1. DO NOT EDIT. - -package models - -import ( - "fmt" - "strings" - - "gopkg.in/reform.v1" - "gopkg.in/reform.v1/parse" -) - -type personTableType struct { - s parse.StructInfo - z []interface{} -} - -// Schema returns a schema name in SQL database (""). -func (v *personTableType) Schema() string { - return v.s.SQLSchema -} - -// Name returns a view or table name in SQL database ("people"). -func (v *personTableType) Name() string { - return v.s.SQLName -} - -// Columns returns a new slice of column names for that view or table in SQL database. -func (v *personTableType) Columns() []string { - return []string{"id", "name", "email", "created_at", "updated_at"} -} - -// NewStruct makes a new struct for that view or table. -func (v *personTableType) NewStruct() reform.Struct { - return new(Person) -} - -// NewRecord makes a new record for that table. -func (v *personTableType) NewRecord() reform.Record { - return new(Person) -} - -// PKColumnIndex returns an index of primary key column for that table in SQL database. -func (v *personTableType) PKColumnIndex() uint { - return uint(v.s.PKFieldIndex) -} - -// PersonTable represents people view or table in SQL database. -var PersonTable = &personTableType{ - s: parse.StructInfo{Type: "Person", SQLSchema: "", SQLName: "people", Fields: []parse.FieldInfo{{Name: "ID", Type: "int32", Column: "id"}, {Name: "Name", Type: "string", Column: "name"}, {Name: "Email", Type: "*string", Column: "email"}, {Name: "CreatedAt", Type: "time.Time", Column: "created_at"}, {Name: "UpdatedAt", Type: "*time.Time", Column: "updated_at"}}, PKFieldIndex: 0}, - z: new(Person).Values(), -} - -// String returns a string representation of this struct or record. -func (s Person) String() string { - res := make([]string, 5) - res[0] = "ID: " + reform.Inspect(s.ID, true) - res[1] = "Name: " + reform.Inspect(s.Name, true) - res[2] = "Email: " + reform.Inspect(s.Email, true) - res[3] = "CreatedAt: " + reform.Inspect(s.CreatedAt, true) - res[4] = "UpdatedAt: " + reform.Inspect(s.UpdatedAt, true) - return strings.Join(res, ", ") -} - -// Values returns a slice of struct or record field values. -// Returned interface{} values are never untyped nils. -func (s *Person) Values() []interface{} { - return []interface{}{ - s.ID, - s.Name, - s.Email, - s.CreatedAt, - s.UpdatedAt, - } -} - -// Pointers returns a slice of pointers to struct or record fields. -// Returned interface{} values are never untyped nils. -func (s *Person) Pointers() []interface{} { - return []interface{}{ - &s.ID, - &s.Name, - &s.Email, - &s.CreatedAt, - &s.UpdatedAt, - } -} - -// View returns View object for that struct. -func (s *Person) View() reform.View { - return PersonTable -} - -// Table returns Table object for that record. -func (s *Person) Table() reform.Table { - return PersonTable -} - -// PKValue returns a value of primary key for that record. -// Returned interface{} value is never untyped nil. -func (s *Person) PKValue() interface{} { - return s.ID -} - -// PKPointer returns a pointer to primary key field for that record. -// Returned interface{} value is never untyped nil. -func (s *Person) PKPointer() interface{} { - return &s.ID -} - -// HasPK returns true if record has non-zero primary key set, false otherwise. -func (s *Person) HasPK() bool { - return s.ID != PersonTable.z[PersonTable.s.PKFieldIndex] -} - -// SetPK sets record primary key. -func (s *Person) SetPK(pk interface{}) { - if i64, ok := pk.(int64); ok { - s.ID = int32(i64) - } else { - s.ID = pk.(int32) - } -} - -// check interfaces -var ( - _ reform.View = PersonTable - _ reform.Struct = (*Person)(nil) - _ reform.Table = PersonTable - _ reform.Record = (*Person)(nil) - _ fmt.Stringer = (*Person)(nil) -) - -func init() { - parse.AssertUpToDate(&PersonTable.s, new(Person)) -} diff --git a/_examples/database/orm/reform/postman_collection.json b/_examples/database/orm/reform/postman_collection.json deleted file mode 100644 index b72a9bd5cb..0000000000 --- a/_examples/database/orm/reform/postman_collection.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "info": { - "_postman_id": "6b66000d-9c04-4d0a-b55c-8a493bf59015", - "name": "iris-reform-example", - "description": "Example API calls for iris reform example.", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "http://localhost:8080/persons", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\r\n \"name\": \"John\",\r\n \"email\": \"example@example.com\"\r\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://localhost:8080/persons", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "persons" - ] - } - }, - "response": [] - }, - { - "name": "http://localhost:8080/persons", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "http://localhost:8080/persons", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "persons" - ] - } - }, - "response": [] - } - ], - "protocolProfileBehavior": {} -} \ No newline at end of file diff --git a/_examples/database/orm/xorm/main.go b/_examples/database/orm/xorm/main.go deleted file mode 100644 index 2419977a05..0000000000 --- a/_examples/database/orm/xorm/main.go +++ /dev/null @@ -1,74 +0,0 @@ -// Package main shows how an orm can be used within your web app -// it just inserts a column and select the first. -package main - -import ( - "time" - - "github.com/kataras/iris/v12" - - "github.com/go-xorm/xorm" - _ "github.com/mattn/go-sqlite3" -) - -/* - go get -u github.com/mattn/go-sqlite3 - go get -u github.com/go-xorm/xorm - - If you're on win64 and you can't install go-sqlite3: - 1. Download: https://sourceforge.net/projects/mingw-w64/files/latest/download - 2. Select "x86_x64" and "posix" - 3. Add C:\Program Files\mingw-w64\x86_64-7.1.0-posix-seh-rt_v5-rev1\mingw64\bin - to your PATH env variable. - - Docs: http://xorm.io/docs/ -*/ - -// User is our user table structure. -type User struct { - ID int64 // auto-increment by-default by xorm - Version string `xorm:"varchar(200)"` - Salt string - Username string - Password string `xorm:"varchar(200)"` - Languages string `xorm:"varchar(200)"` - CreatedAt time.Time `xorm:"created"` - UpdatedAt time.Time `xorm:"updated"` -} - -func main() { - app := iris.New() - - orm, err := xorm.NewEngine("sqlite3", "./test.db") - if err != nil { - app.Logger().Fatalf("orm failed to initialized: %v", err) - } - - iris.RegisterOnInterrupt(func() { - orm.Close() - }) - - err = orm.Sync2(new(User)) - - if err != nil { - app.Logger().Fatalf("orm failed to initialized User table: %v", err) - } - - app.Get("/insert", func(ctx iris.Context) { - user := &User{Username: "kataras", Salt: "hash---", Password: "hashed", CreatedAt: time.Now(), UpdatedAt: time.Now()} - orm.Insert(user) - - ctx.Writef("user inserted: %#v", user) - }) - - app.Get("/get", func(ctx iris.Context) { - user := User{ID: 1} - if ok, _ := orm.Get(&user); ok { - ctx.Writef("user found: %#v", user) - } - }) - - // http://localhost:8080/insert - // http://localhost:8080/get - app.Listen(":8080") -} diff --git a/_examples/dependency-injection/basic/main.go b/_examples/dependency-injection/basic/main.go deleted file mode 100644 index 58138fa549..0000000000 --- a/_examples/dependency-injection/basic/main.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - - "github.com/go-playground/validator/v10" -) - -type ( - testInput struct { - Email string `json:"email" validate:"required"` - } - - testOutput struct { - ID int `json:"id"` - Name string `json:"name"` - } -) - -func handler(id int, in testInput) testOutput { - return testOutput{ - ID: id, - Name: in.Email, - } -} - -func configureAPI(api *iris.APIContainer) { - /* Here is how you can inject a return value from a handler, - in this case the "testOutput": - api.UseResultHandler(func(next iris.ResultHandler) iris.ResultHandler { - return func(ctx iris.Context, v interface{}) error { - return next(ctx, map[string]interface{}{"injected": true}) - } - }) - */ - - api.Post("/{id:int}", handler) -} - -func main() { - app := iris.New() - app.Validator = validator.New() - app.Logger().SetLevel("debug") - - app.ConfigureContainer(configureAPI) - app.Listen(":8080") -} diff --git a/_examples/dependency-injection/basic/middleware/main.go b/_examples/dependency-injection/basic/middleware/main.go deleted file mode 100644 index 6faf607dcf..0000000000 --- a/_examples/dependency-injection/basic/middleware/main.go +++ /dev/null @@ -1,64 +0,0 @@ -package main - -import ( - "errors" - - "github.com/kataras/iris/v12" -) - -type ( - testInput struct { - Email string `json:"email"` - } - - testOutput struct { - ID int `json:"id"` - Name string `json:"name"` - } -) - -func handler(id int, in testInput) testOutput { - return testOutput{ - ID: id, - Name: in.Email, - } -} - -var errCustom = errors.New("my_error") - -func middleware(in testInput) (int, error) { - if in.Email == "invalid" { - // stop the execution and don't continue to "handler" - // without firing an error. - return iris.StatusAccepted, iris.ErrStopExecution - } else if in.Email == "error" { - // stop the execution and fire a custom error. - return iris.StatusConflict, errCustom - } - - return iris.StatusOK, nil -} - -func newApp() *iris.Application { - app := iris.New() - - // handle the route, respond with - // a JSON and 200 status code - // or 202 status code and empty body - // or a 409 status code and "my_error" body. - app.ConfigureContainer(func(api *iris.APIContainer) { - api.Use(middleware) - api.Post("/{id:int}", handler) - }) - - app.Configure( - iris.WithOptimizations, /* optional */ - iris.WithoutBodyConsumptionOnUnmarshal /* required when more than one handler is consuming request payload(testInput) */) - - return app -} - -func main() { - app := newApp() - app.Listen(":8080") -} diff --git a/_examples/dependency-injection/basic/middleware/main_test.go b/_examples/dependency-injection/basic/middleware/main_test.go deleted file mode 100644 index eb90c87b51..0000000000 --- a/_examples/dependency-injection/basic/middleware/main_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestDependencyInjectionBasic_Middleware(t *testing.T) { - app := newApp() - - e := httptest.New(t, app) - e.POST("/42").WithJSON(testInput{Email: "my_email"}).Expect(). - Status(httptest.StatusOK). - JSON().Equal(testOutput{ID: 42, Name: "my_email"}) - - // it should stop the execution at the middleware and return the middleware's status code, - // because the error is `ErrStopExecution`. - e.POST("/42").WithJSON(testInput{Email: "invalid"}).Expect(). - Status(httptest.StatusAccepted).Body().Empty() - - // it should stop the execution at the middleware and return the error's text. - e.POST("/42").WithJSON(testInput{Email: "error"}).Expect(). - Status(httptest.StatusConflict).Body().Equal("my_error") -} diff --git a/_examples/dependency-injection/context-register-dependency/main.go b/_examples/dependency-injection/context-register-dependency/main.go deleted file mode 100644 index 8d73bbae86..0000000000 --- a/_examples/dependency-injection/context-register-dependency/main.go +++ /dev/null @@ -1,95 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - app.Use(RoleMiddleware) - - app.Get("/", commonHandler) - c := app.ConfigureContainer() - /* - When you do NOT have access to the middleware code itself - then you can register a request dependency - which retrieves the value from the Context - and returns it, so handler/function's input arguments - with that `Role` type can be binded. - - c.RegisterDependency(func(ctx iris.Context) Role { - role, ok := GetRole(ctx) - if !ok { - // This codeblock will never be executed here - // but you can stop executing a handler which depends on - // that dependency with `ctx.StopExecution/ctx.StopWithXXX` methods - // or by returning a second output argument of `error` type. - ctx.StopExecution() - return Role{} - } - - return role - }) - */ - c.Get("/dep", handlerWithDependencies) - - // http://localhost:8080?name=kataras - // http://localhost:8080/dep?name=kataras - app.Listen(":8080") -} - -func commonHandler(ctx iris.Context) { - role, _ := GetRole(ctx) - ctx.WriteString(role.Name) -} - -func handlerWithDependencies(role Role) string { - return role.Name -} - -// Code for an example middleware. - -// Role struct value example. -type Role struct { - Name string -} - -const roleContextKey = "myapp.role" - -// RoleMiddleware example of a custom middleware. -func RoleMiddleware(ctx iris.Context) { - // [do it yourself: extract the role from the request...] - if ctx.URLParam("name") != "kataras" { - ctx.StopWithStatus(iris.StatusUnauthorized) - return - } - // - - role := Role{Name: "admin"} - - ctx.Values().Set(roleContextKey, role) - - // When you have access to the middleware itself: - // Use the `RegisterDependency` to register - // struct type values as dependencies at request-time for - // any potential dependency injection-ed user handler. - // This way the user of your middleware can get rid of - // manually register a dependency for that `Role` type with calls of - // `APIContainer.RegisterDependency` (and `mvc.Application.Register`). - ctx.RegisterDependency(role) - - ctx.Next() -} - -// GetRole returns the role inside the context values, -// the `roleMiddleware` should be executed first. -func GetRole(ctx iris.Context) (Role, bool) { - v := ctx.Values().Get(roleContextKey) - if v != nil { - if role, ok := v.(Role); ok { - return role, true - } - } - - return Role{}, false -} - -// End Code of our example middleware. diff --git a/_examples/dependency-injection/jwt/contrib/go.mod b/_examples/dependency-injection/jwt/contrib/go.mod deleted file mode 100644 index 228fc1397e..0000000000 --- a/_examples/dependency-injection/jwt/contrib/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/kataras/iris/_examples/dependency-injection/jwt/contrib - -go 1.15 - -require github.com/iris-contrib/middleware/jwt v0.0.0-20200710202437-92b01b85baaf diff --git a/_examples/dependency-injection/jwt/contrib/main.go b/_examples/dependency-injection/jwt/contrib/main.go deleted file mode 100644 index 171f2ed19b..0000000000 --- a/_examples/dependency-injection/jwt/contrib/main.go +++ /dev/null @@ -1,66 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - - "github.com/iris-contrib/middleware/jwt" -) - -var secret = []byte("My Secret Key") - -func main() { - app := iris.New() - app.ConfigureContainer(register) - - app.Listen(":8080") -} - -func register(api *iris.APIContainer) { - j := jwt.New(jwt.Config{ - // Extract by "token" url parameter. - Extractor: jwt.FromFirst(jwt.FromParameter("token"), jwt.FromAuthHeader), - ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) { - return secret, nil - }, - SigningMethod: jwt.SigningMethodHS256, - }) - - api.Get("/authenticate", writeToken) - // This works as usually: - api.Get("/restricted", j.Serve, restrictedPage) - - // You can also bind the *jwt.Token (see `verifiedWithBindedTokenPage`) - // by registering a *jwt.Token dependency. - // - // api.RegisterDependency(func(ctx iris.Context) *jwt.Token { - // if err := j.CheckJWT(ctx); err != nil { - // ctx.StopWithStatus(iris.StatusUnauthorized) - // return nil - // } - // - // token := j.Get(ctx) - // return token - // }) - // ^ You can do the same with MVC too, as the container is shared and works - // the same way in both functions-as-handlers and structs-as-controllers. - // - // api.Get("/", restrictedPageWithBindedTokenPage) -} - -func writeToken() string { - token := jwt.NewTokenWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "foo": "bar", - }) - - tokenString, _ := token.SignedString(secret) - return tokenString -} - -func restrictedPage() string { - return "This page can only be seen by verified clients" -} - -func restrictedPageWithBindedTokenPage(token *jwt.Token) string { - // Token[foo] value: bar - return "Token[foo] value: " + token.Claims.(jwt.MapClaims)["foo"].(string) -} diff --git a/_examples/dependency-injection/jwt/main.go b/_examples/dependency-injection/jwt/main.go deleted file mode 100644 index c705fac9a8..0000000000 --- a/_examples/dependency-injection/jwt/main.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/jwt" -) - -func main() { - app := iris.New() - app.ConfigureContainer(register) - - app.Listen(":8080") -} - -func register(api *iris.APIContainer) { - j := jwt.HMAC(15*time.Minute, "secret", "secretforencrypt") - - api.RegisterDependency(func(ctx iris.Context) (claims userClaims) { - if err := j.VerifyToken(ctx, &claims); err != nil { - ctx.StopWithError(iris.StatusUnauthorized, err) - return - } - - return - }) - - api.Get("/authenticate", writeToken(j)) - api.Get("/restricted", restrictedPage) -} - -type userClaims struct { - jwt.Claims - Username string -} - -func writeToken(j *jwt.JWT) iris.Handler { - return func(ctx iris.Context) { - j.WriteToken(ctx, userClaims{ - Claims: j.Expiry(jwt.Claims{Issuer: "an-issuer"}), - Username: "kataras", - }) - } -} - -func restrictedPage(claims userClaims) string { - // userClaims.Username: kataras - return "userClaims.Username: " + claims.Username -} diff --git a/_examples/dependency-injection/overview/datamodels/movie.go b/_examples/dependency-injection/overview/datamodels/movie.go deleted file mode 100644 index 1b25d127d7..0000000000 --- a/_examples/dependency-injection/overview/datamodels/movie.go +++ /dev/null @@ -1,18 +0,0 @@ -// file: datamodels/movie.go - -package datamodels - -// Movie is our sample data structure. -// Keep note that the tags for public-use (for our web app) -// should be kept in other file like "web/viewmodels/movie.go" -// which could wrap by embedding the datamodels.Movie or -// declare new fields instead butwe will use this datamodel -// as the only one Movie model in our application, -// for the sake of simplicty. -type Movie struct { - ID uint64 `json:"id"` - Name string `json:"name"` - Year int `json:"year"` - Genre string `json:"genre"` - Poster string `json:"poster"` -} diff --git a/_examples/dependency-injection/overview/datasource/movies.go b/_examples/dependency-injection/overview/datasource/movies.go deleted file mode 100644 index 4b5fbf3169..0000000000 --- a/_examples/dependency-injection/overview/datasource/movies.go +++ /dev/null @@ -1,44 +0,0 @@ -// file: datasource/movies.go - -package datasource - -import "github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels" - -// Movies is our imaginary data source. -var Movies = map[uint64]datamodels.Movie{ - 1: { - ID: 1, - Name: "Casablanca", - Year: 1942, - Genre: "Romance", - Poster: "https://iris-go.com/images/examples/mvc-movies/1.jpg", - }, - 2: { - ID: 2, - Name: "Gone with the Wind", - Year: 1939, - Genre: "Romance", - Poster: "https://iris-go.com/images/examples/mvc-movies/2.jpg", - }, - 3: { - ID: 3, - Name: "Citizen Kane", - Year: 1941, - Genre: "Mystery", - Poster: "https://iris-go.com/images/examples/mvc-movies/3.jpg", - }, - 4: { - ID: 4, - Name: "The Wizard of Oz", - Year: 1939, - Genre: "Fantasy", - Poster: "https://iris-go.com/images/examples/mvc-movies/4.jpg", - }, - 5: { - ID: 5, - Name: "North by Northwest", - Year: 1959, - Genre: "Thriller", - Poster: "https://iris-go.com/images/examples/mvc-movies/5.jpg", - }, -} diff --git a/_examples/dependency-injection/overview/main.go b/_examples/dependency-injection/overview/main.go deleted file mode 100644 index 8386aaae38..0000000000 --- a/_examples/dependency-injection/overview/main.go +++ /dev/null @@ -1,55 +0,0 @@ -// file: main.go - -package main - -import ( - "github.com/kataras/iris/v12/_examples/dependency-injection/overview/datasource" - "github.com/kataras/iris/v12/_examples/dependency-injection/overview/repositories" - "github.com/kataras/iris/v12/_examples/dependency-injection/overview/services" - "github.com/kataras/iris/v12/_examples/dependency-injection/overview/web/middleware" - "github.com/kataras/iris/v12/_examples/dependency-injection/overview/web/routes" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - app.Logger().SetLevel("debug") - - // Load the template files. - app.RegisterView(iris.HTML("./web/views", ".html")) - - // Create our movie repository with some (memory) data from the datasource. - repo := repositories.NewMovieRepository(datasource.Movies) - - app.Party("/hello").ConfigureContainer(func(r *iris.APIContainer) { - r.Get("/", routes.Hello) - r.Get("/{name}", routes.HelloName) - }) - - app.Party("/movies").ConfigureContainer(func(r *iris.APIContainer) { - // Create our movie service, we will bind it to the movie app's dependencies. - movieService := services.NewMovieService(repo) - r.RegisterDependency(movieService) - - // Add the basic authentication(admin:password) middleware - // for the /movies based requests. - r.Use(middleware.BasicAuth) - - r.Get("/", routes.Movies) - r.Get("/{id:uint64}", routes.MovieByID) - r.Put("/{id:uint64}", routes.UpdateMovieByID) - r.Delete("/{id:uint64}", routes.DeleteMovieByID) - }) - - // http://localhost:8080/hello - // http://localhost:8080/hello/iris - // http://localhost:8080/movies ("admin": "password") - // http://localhost:8080/movies/1 - app.Listen( - // Start the web server at localhost:8080 - "localhost:8080", - // enables faster json serialization and more: - iris.WithOptimizations, - ) -} diff --git a/_examples/dependency-injection/overview/repositories/movie_repository.go b/_examples/dependency-injection/overview/repositories/movie_repository.go deleted file mode 100644 index 0a46cb1bba..0000000000 --- a/_examples/dependency-injection/overview/repositories/movie_repository.go +++ /dev/null @@ -1,176 +0,0 @@ -// file: repositories/movie_repository.go - -package repositories - -import ( - "errors" - "sync" - - "github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels" -) - -// Query represents the visitor and action queries. -type Query func(datamodels.Movie) bool - -// MovieRepository handles the basic operations of a movie entity/model. -// It's an interface in order to be testable, i.e a memory movie repository or -// a connected to an sql database. -type MovieRepository interface { - Exec(query Query, action Query, limit int, mode int) (ok bool) - - Select(query Query) (movie datamodels.Movie, found bool) - SelectMany(query Query, limit int) (results []datamodels.Movie) - - InsertOrUpdate(movie datamodels.Movie) (updatedMovie datamodels.Movie, err error) - Delete(query Query, limit int) (deleted bool) -} - -// NewMovieRepository returns a new movie memory-based repository, -// the one and only repository type in our example. -func NewMovieRepository(source map[uint64]datamodels.Movie) MovieRepository { - return &movieMemoryRepository{source: source} -} - -// movieMemoryRepository is a "MovieRepository" -// which manages the movies using the memory data source (map). -type movieMemoryRepository struct { - source map[uint64]datamodels.Movie - mu sync.RWMutex -} - -const ( - // ReadOnlyMode will RLock(read) the data . - ReadOnlyMode = iota - // ReadWriteMode will Lock(read/write) the data. - ReadWriteMode -) - -func (r *movieMemoryRepository) Exec(query Query, action Query, actionLimit int, mode int) (ok bool) { - loops := 0 - - if mode == ReadOnlyMode { - r.mu.RLock() - defer r.mu.RUnlock() - } else { - r.mu.Lock() - defer r.mu.Unlock() - } - - for _, movie := range r.source { - ok = query(movie) - if ok { - if action(movie) { - loops++ - if actionLimit >= loops { - break // break - } - } - } - } - - return -} - -// Select receives a query function -// which is fired for every single movie model inside -// our imaginary data source. -// When that function returns true then it stops the iteration. -// -// It returns the query's return last known "found" value -// and the last known movie model -// to help callers to reduce the LOC. -// -// It's actually a simple but very clever prototype function -// I'm using everywhere since I firstly think of it, -// hope you'll find it very useful as well. -func (r *movieMemoryRepository) Select(query Query) (movie datamodels.Movie, found bool) { - found = r.Exec(query, func(m datamodels.Movie) bool { - movie = m - return true - }, 1, ReadOnlyMode) - - // set an empty datamodels.Movie if not found at all. - if !found { - movie = datamodels.Movie{} - } - - return -} - -// SelectMany same as Select but returns one or more datamodels.Movie as a slice. -// If limit <=0 then it returns everything. -func (r *movieMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.Movie) { - r.Exec(query, func(m datamodels.Movie) bool { - results = append(results, m) - return true - }, limit, ReadOnlyMode) - - return -} - -// InsertOrUpdate adds or updates a movie to the (memory) storage. -// -// Returns the new movie and an error if any. -func (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamodels.Movie, error) { - id := movie.ID - - if id == 0 { // Create new action - var lastID uint64 - // find the biggest ID in order to not have duplications - // in productions apps you can use a third-party - // library to generate a UUID as string. - r.mu.RLock() - for _, item := range r.source { - if item.ID > lastID { - lastID = item.ID - } - } - r.mu.RUnlock() - - id = lastID + 1 - movie.ID = id - - // map-specific thing - r.mu.Lock() - r.source[id] = movie - r.mu.Unlock() - - return movie, nil - } - - // Update action based on the movie.ID, - // here we will allow updating the poster and genre if not empty. - // Alternatively we could do pure replace instead: - // r.source[id] = movie - // and comment the code below; - current, exists := r.Select(func(m datamodels.Movie) bool { - return m.ID == id - }) - - if !exists { // ID is not a real one, return an error. - return datamodels.Movie{}, errors.New("failed to update a nonexistent movie") - } - - // or comment these and r.source[id] = m for pure replace - if movie.Poster != "" { - current.Poster = movie.Poster - } - - if movie.Genre != "" { - current.Genre = movie.Genre - } - - // map-specific thing - r.mu.Lock() - r.source[id] = current - r.mu.Unlock() - - return movie, nil -} - -func (r *movieMemoryRepository) Delete(query Query, limit int) bool { - return r.Exec(query, func(m datamodels.Movie) bool { - delete(r.source, m.ID) - return true - }, limit, ReadWriteMode) -} diff --git a/_examples/dependency-injection/overview/services/movie_service.go b/_examples/dependency-injection/overview/services/movie_service.go deleted file mode 100644 index 6835a9b51a..0000000000 --- a/_examples/dependency-injection/overview/services/movie_service.go +++ /dev/null @@ -1,65 +0,0 @@ -// file: services/movie_service.go - -package services - -import ( - "github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels" - "github.com/kataras/iris/v12/_examples/dependency-injection/overview/repositories" -) - -// MovieService handles some of the CRUID operations of the movie datamodel. -// It depends on a movie repository for its actions. -// It's here to decouple the data source from the higher level compoments. -// As a result a different repository type can be used with the same logic without any aditional changes. -// It's an interface and it's used as interface everywhere -// because we may need to change or try an experimental different domain logic at the future. -type MovieService interface { - GetAll() []datamodels.Movie - GetByID(id uint64) (datamodels.Movie, bool) - DeleteByID(id uint64) bool - UpdatePosterAndGenreByID(id uint64, poster string, genre string) (datamodels.Movie, error) -} - -// NewMovieService returns the default movie service. -func NewMovieService(repo repositories.MovieRepository) MovieService { - return &movieService{ - repo: repo, - } -} - -type movieService struct { - repo repositories.MovieRepository -} - -// GetAll returns all movies. -func (s *movieService) GetAll() []datamodels.Movie { - return s.repo.SelectMany(func(_ datamodels.Movie) bool { - return true - }, -1) -} - -// GetByID returns a movie based on its id. -func (s *movieService) GetByID(id uint64) (datamodels.Movie, bool) { - return s.repo.Select(func(m datamodels.Movie) bool { - return m.ID == id - }) -} - -// UpdatePosterAndGenreByID updates a movie's poster and genre. -func (s *movieService) UpdatePosterAndGenreByID(id uint64, poster string, genre string) (datamodels.Movie, error) { - // update the movie and return it. - return s.repo.InsertOrUpdate(datamodels.Movie{ - ID: id, - Poster: poster, - Genre: genre, - }) -} - -// DeleteByID deletes a movie by its id. -// -// Returns true if deleted otherwise false. -func (s *movieService) DeleteByID(id uint64) bool { - return s.repo.Delete(func(m datamodels.Movie) bool { - return m.ID == id - }, 1) -} diff --git a/_examples/dependency-injection/overview/web/middleware/basicauth.go b/_examples/dependency-injection/overview/web/middleware/basicauth.go deleted file mode 100644 index 5d61fc727e..0000000000 --- a/_examples/dependency-injection/overview/web/middleware/basicauth.go +++ /dev/null @@ -1,12 +0,0 @@ -// file: web/middleware/basicauth.go - -package middleware - -import "github.com/kataras/iris/v12/middleware/basicauth" - -// BasicAuth middleware sample. -var BasicAuth = basicauth.New(basicauth.Config{ - Users: map[string]string{ - "admin": "password", - }, -}) diff --git a/_examples/dependency-injection/overview/web/routes/hello.go b/_examples/dependency-injection/overview/web/routes/hello.go deleted file mode 100644 index e0c4a0296e..0000000000 --- a/_examples/dependency-injection/overview/web/routes/hello.go +++ /dev/null @@ -1,50 +0,0 @@ -// file: web/routes/hello.go - -package routes - -import ( - "errors" - - "github.com/kataras/iris/v12/hero" -) - -var helloView = hero.View{ - Name: "hello/index.html", - Data: map[string]interface{}{ - "Title": "Hello Page", - "MyMessage": "Welcome to my awesome website", - }, -} - -// Hello will return a predefined view with bind data. -// -// `hero.Result` is just an interface with a `Dispatch` function. -// `hero.Response` and `hero.View` are the builtin result type dispatchers -// you can even create custom response dispatchers by -// implementing the `github.com/kataras/iris/hero#Result` interface. -func Hello() hero.Result { - return helloView -} - -// you can define a standard error in order to re-use anywhere in your app. -var errBadName = errors.New("bad name") - -// you can just return it as error or even better -// wrap this error with an hero.Response to make it an hero.Result compatible type. -var badName = hero.Response{Err: errBadName, Code: 400} - -// HelloName returns a "Hello {name}" response. -// Demos: -// curl -i http://localhost:8080/hello/iris -// curl -i http://localhost:8080/hello/anything -func HelloName(name string) hero.Result { - if name != "iris" { - return badName - } - - // return hero.Response{Text: "Hello " + name} OR: - return hero.View{ - Name: "hello/name.html", - Data: name, - } -} diff --git a/_examples/dependency-injection/overview/web/routes/movies.go b/_examples/dependency-injection/overview/web/routes/movies.go deleted file mode 100644 index d6b902d98a..0000000000 --- a/_examples/dependency-injection/overview/web/routes/movies.go +++ /dev/null @@ -1,59 +0,0 @@ -// file: web/routes/movie.go - -package routes - -import ( - "errors" - - "github.com/kataras/iris/v12/_examples/dependency-injection/overview/datamodels" - "github.com/kataras/iris/v12/_examples/dependency-injection/overview/services" - - "github.com/kataras/iris/v12" -) - -// Movies returns list of the movies. -// Demo: -// curl -i http://localhost:8080/movies -func Movies(service services.MovieService) (results []datamodels.Movie) { - return service.GetAll() -} - -// MovieByID returns a movie. -// Demo: -// curl -i http://localhost:8080/movies/1 -func MovieByID(service services.MovieService, id uint64) (movie datamodels.Movie, found bool) { - return service.GetByID(id) // it will throw 404 if not found. -} - -// UpdateMovieByID updates a movie. -// Demo: -// curl -i -X PUT -F "genre=Thriller" -F "poster=@/Users/kataras/Downloads/out.gif" http://localhost:8080/movies/1 -func UpdateMovieByID(ctx iris.Context, service services.MovieService, id uint64) (datamodels.Movie, error) { - // get the request data for poster and genre - file, info, err := ctx.FormFile("poster") - if err != nil { - return datamodels.Movie{}, errors.New("failed due form file 'poster' missing") - } - // we don't need the file so close it now. - file.Close() - - // imagine that is the url of the uploaded file... - poster := info.Filename - genre := ctx.FormValue("genre") - - return service.UpdatePosterAndGenreByID(id, poster, genre) -} - -// DeleteMovieByID deletes a movie. -// Demo: -// curl -i -X DELETE -u admin:password http://localhost:8080/movies/1 -func DeleteMovieByID(service services.MovieService, id uint64) interface{} { - wasDel := service.DeleteByID(id) - if wasDel { - // return the deleted movie's ID - return iris.Map{"deleted": id} - } - // right here we can see that a method function can return any of those two types(map or int), - // we don't have to specify the return type to a specific type. - return iris.StatusBadRequest -} diff --git a/_examples/dependency-injection/overview/web/views/hello/index.html b/_examples/dependency-injection/overview/web/views/hello/index.html deleted file mode 100644 index 9e7b03d6d0..0000000000 --- a/_examples/dependency-injection/overview/web/views/hello/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - {{.Title}} - My App - - - -

{{.MyMessage}}

- - - \ No newline at end of file diff --git a/_examples/dependency-injection/overview/web/views/hello/name.html b/_examples/dependency-injection/overview/web/views/hello/name.html deleted file mode 100644 index d6dd5ac66a..0000000000 --- a/_examples/dependency-injection/overview/web/views/hello/name.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - {{.}}' Portfolio - My App - - - -

Hello {{.}}

- - - \ No newline at end of file diff --git a/_examples/dependency-injection/sessions/main.go b/_examples/dependency-injection/sessions/main.go deleted file mode 100644 index 2172c34c0e..0000000000 --- a/_examples/dependency-injection/sessions/main.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12/_examples/dependency-injection/sessions/routes" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/sessions" -) - -func main() { - app := iris.New() - sessionManager := sessions.New(sessions.Config{ - Cookie: "site_session_id", - Expires: 60 * time.Minute, - AllowReclaim: true, - }) - - // Session is automatically binded through `sessions.Get(ctx)` - // if a *sessions.Session input argument is present on the handler's function, - // which `routes.Index` does. - app.Use(sessionManager.Handler()) - - // Method: GET - // Path: http://localhost:8080 - app.ConfigureContainer(registerRoutes) - - app.Listen(":8080") -} - -func registerRoutes(api *iris.APIContainer) { - api.Get("/", routes.Index) -} diff --git a/_examples/dependency-injection/sessions/routes/index.go b/_examples/dependency-injection/sessions/routes/index.go deleted file mode 100644 index d1660563a1..0000000000 --- a/_examples/dependency-injection/sessions/routes/index.go +++ /dev/null @@ -1,17 +0,0 @@ -package routes - -import ( - "fmt" - - "github.com/kataras/iris/v12/sessions" -) - -// Index will increment a simple int version based on the visits that this user/session did. -func Index(session *sessions.Session) string { - // it increments a "visits" value of integer by one, - // if the entry with key 'visits' doesn't exist it will create it for you. - visits := session.Increment("visits", 1) - - // write the current, updated visits. - return fmt.Sprintf("%d visit(s) from my current session", visits) -} diff --git a/_examples/dependency-injection/smart-contract/main.go b/_examples/dependency-injection/smart-contract/main.go deleted file mode 100644 index f3c93f336a..0000000000 --- a/_examples/dependency-injection/smart-contract/main.go +++ /dev/null @@ -1,156 +0,0 @@ -package main - -import ( - "fmt" - "strings" - - "github.com/kataras/iris/v12" - - // External package to optionally filter JSON responses before sent, - // see `sendJSON` for more. - "github.com/jmespath/go-jmespath" -) - -/* - $ go get github.com/jmespath/go-jmespath -*/ - -func main() { - app := newApp() - app.Logger().SetLevel("debug") - - // http://localhost:8080/users?query=[?Name == 'John Doe'].Age - // <- client will receive the age of a user which his name is "John Doe". - // You can also test query=[0].Name to retrieve the first user's name. - // Or even query=[0:3].Age to print the first three ages. - // Learn more about jmespath and how to filter: - // http://jmespath.readthedocs.io/en/latest/ and - // https://github.com/jmespath/go-jmespath/tree/master/fuzz/testdata - // - // http://localhost:8080/users - // http://localhost:8080/users/William%20Woe - // http://localhost:8080/users/William%20Woe/age - app.Listen(":8080") -} - -func newApp() *iris.Application { - app := iris.New() - - // PartyFunc is the same as usersRouter := app.Party("/users") - // but it gives us an easy way to call router's registration functions, - // i.e functions from another package that can handle this group of routes. - app.PartyFunc("/users", registerUsersRoutes) - - return app -} - -/* - START OF USERS ROUTER -*/ - -func registerUsersRoutes(usersRouter iris.Party) { - // GET: /users - usersRouter.Get("/", getAllUsersHandler) - usersRouter.Party("/{name}").ConfigureContainer(registerUserRoutes) -} - -type user struct { - Name string `json:"name"` - Age int `json:"age"` -} - -var usersSample = []*user{ - {"William Woe", 25}, - {"Mary Moe", 15}, - {"John Doe", 17}, -} - -func getAllUsersHandler(ctx iris.Context) { - err := sendJSON(ctx, usersSample) - if err != nil { - fail(ctx, iris.StatusInternalServerError, "unable to send a list of all users: %v", err) - return - } -} - -/* - START OF USERS.USER SUB ROUTER -*/ - -func registerUserRoutes(userRouter *iris.APIContainer) { - userRouter.RegisterDependency(userDependency) - // GET: /users/{name:string} - userRouter.Get("/", getUserHandler) - // GET: /users/{name:string}/age - userRouter.Get("/age", getUserAgeHandler) -} - -var userDependency = func(ctx iris.Context) *user { - name := strings.Title(ctx.Params().Get("name")) - for _, u := range usersSample { - if u.Name == name { - return u - } - } - - // you may want or no to handle the error here, either way the main route handler - // is going to be executed, always. A dynamic dependency(per-request) is not a middleware, so things like `ctx.Next()` or `ctx.StopExecution()` - // do not apply here, look the `getUserHandler`'s first lines; we stop/exit the handler manually - // if the received user is nil but depending on your app's needs, it is possible to do other things too. - // A dynamic dependency like this can return more output values, i.e (*user, bool). - fail(ctx, iris.StatusNotFound, "user with name '%s' not found", name) - return nil -} - -func getUserHandler(ctx iris.Context, u *user) { - sendJSON(ctx, u) -} - -func getUserAgeHandler(u *user) string { - return fmt.Sprintf("%d", u.Age) -} - -/* END OF USERS.USER SUB ROUTER */ - -/* END OF USERS ROUTER */ - -// common JSON response for manual HTTP errors, optionally. -type httpError struct { - Code int `json:"code"` - Reason string `json:"reason"` -} - -func (h httpError) Error() string { - return fmt.Sprintf("Status Code: %d\nReason: %s", h.Code, h.Reason) -} - -func fail(ctx iris.Context, statusCode int, format string, a ...interface{}) { - err := httpError{ - Code: statusCode, - Reason: fmt.Sprintf(format, a...), - } - - // log all the >= 500 internal errors. - if statusCode >= 500 { - ctx.Application().Logger().Error(err) - } - - // no next handlers will run. - ctx.StopWithJSON(statusCode, err) -} - -// JSON helper to give end-user the ability to put indention chars or filtering the response, you can do that, optionally. -// If you'd like to see that function inside the Iris' Context itself raise a [Feature Request] issue and link this example. -func sendJSON(ctx iris.Context, resp interface{}) (err error) { - indent := ctx.URLParamDefault("indent", " ") - // i.e [?Name == 'John Doe'].Age # to output the [age] of a user which his name is "John Doe". - if query := ctx.URLParam("query"); query != "" && query != "[]" { - resp, err = jmespath.Search(query, resp) - if err != nil { - return - } - } - - _, err = ctx.JSON(resp, iris.JSON{Indent: indent, UnescapeHTML: true}) - return err -} diff --git a/_examples/desktop/blink/main.go b/_examples/desktop/blink/main.go deleted file mode 100644 index a771727f0c..0000000000 --- a/_examples/desktop/blink/main.go +++ /dev/null @@ -1,42 +0,0 @@ -// +build windows - -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/raintean/blink" -) - -const addr = "127.0.0.1:8080" - -/* - $ go build -ldflags -H=windowsgui -o myapp.exe - $ ./myapp.exe # run the app -*/ -func main() { - go runServer() - showAndWaitWindow() -} - -func runServer() { - app := iris.New() - app.Get("/", func(ctx iris.Context) { - ctx.HTML("

Hello Desktop

") - }) - app.Listen(addr) -} - -func showAndWaitWindow() { - blink.SetDebugMode(true) - if err := blink.InitBlink(); err != nil { - panic(err) - } - - view := blink.NewWebView(false, 800, 600) - view.LoadURL(addr) - view.SetWindowTitle("My App") - view.MoveToCenter() - view.ShowWindow() - - <-view.Destroy -} diff --git a/_examples/desktop/lorca/main.go b/_examples/desktop/lorca/main.go deleted file mode 100644 index eb5fe18f74..0000000000 --- a/_examples/desktop/lorca/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/zserge/lorca" -) - -const addr = "127.0.0.1:8080" - -/* - $ go build -ldflags="-H windowsgui" -o myapp.exe # build for windows - $ ./myapp.exe # run -*/ -func main() { - go runServer() - showAndWaitWindow() -} - -func runServer() { - app := iris.New() - app.Get("/", func(ctx iris.Context) { - ctx.HTML("My App

Hello Desktop

") - }) - app.Listen(addr) -} - -func showAndWaitWindow() { - webview, err := lorca.New("http://"+addr, "", 800, 600) - if err != nil { - panic(err) - } - defer webview.Close() - - // webview.SetBounds(lorca.Bounds{ - // WindowState: lorca.WindowStateFullscreen, - // }) - - // Wait for the browser window to be closed - <-webview.Done() -} diff --git a/_examples/desktop/webview/main.go b/_examples/desktop/webview/main.go deleted file mode 100644 index e354a953eb..0000000000 --- a/_examples/desktop/webview/main.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/webview/webview" -) - -const addr = "127.0.0.1:8080" - -/* - # Windows requires special linker flags for GUI apps. - # It's also recommended to use TDM-GCC-64 compiler for CGo. - # http://tdm-gcc.tdragon.net/download - # - # - $ go build -ldflags="-H windowsgui" -o myapp.exe # build for windows - $ ./myapp.exe # run - # - # - # Note: if you see "use option -std=c99 or -std=gnu99 to compile your code" - # please refer to: https://github.com/webview/webview/issues/188 -*/ -func main() { - go runServer() - showAndWaitWindow() -} - -func runServer() { - app := iris.New() - app.Get("/", func(ctx iris.Context) { - ctx.HTML("

Hello Desktop

") - }) - app.Listen(addr) -} - -func showAndWaitWindow() { - debug := true - w := webview.New(debug) - defer w.Destroy() - w.SetTitle("Minimal webview example") - w.SetSize(800, 600, webview.HintNone) - w.Navigate("https://iris-go.com") - w.Run() -} diff --git a/_examples/dropzonejs/README.md b/_examples/dropzonejs/README.md deleted file mode 100644 index d2ec00fc8f..0000000000 --- a/_examples/dropzonejs/README.md +++ /dev/null @@ -1,168 +0,0 @@ -# Articles - -* [How to build a file upload form using DropzoneJS and Go](https://hackernoon.com/how-to-build-a-file-upload-form-using-dropzonejs-and-go-8fb9f258a991) -* [How to display existing files on server using DropzoneJS and Go](https://hackernoon.com/how-to-display-existing-files-on-server-using-dropzonejs-and-go-53e24b57ba19) - -# Content - -This is the part 1 of 2 in DropzoneJS + Go series. - -- [Part 1: How to build a file upload form](README.md) -- [Part 2: How to display existing files on server](README_PART2.md) - -# DropzoneJS + Go: How to build a file upload form - -[DropzoneJS](https://github.com/enyo/dropzone) is an open source library that provides drag'n'drop file uploads with image previews. It is a great JavaScript library which actually does not even rely on JQuery. -In this tutorial, we are building a multiple file upload form using DropzoneJS, and the backend will be handled by Go and [Iris](https://iris-go.com). - -## Table Of Content - -- [Preparation](#preparation) -- [Work with DropzoneJS](#work-with-dropzonejs) -- [Work with Go](#work-with-go) - -## Preparation - -1. Download [Go(Golang)](https://golang.org/dl), setup your computer as shown there and continue to 2. -2. Install [Iris](https://github.com/kataras/iris); open a terminal and execute `go get -u github.com/kataras/iris` -3. Download DropzoneJS from [this URL](https://raw.githubusercontent.com/enyo/dropzone/master/dist/dropzone.js). DropzoneJS does not rely on JQuery, you will not have to worry that, upgrading JQuery version breaks your application. -4. Download dropzone.css from [this URL](https://raw.githubusercontent.com/enyo/dropzone/master/dist/dropzone.css), if you want some already made css. -5. Create a folder "./public/uploads", this is for storing uploaded files. -6. Create a file "./views/upload.html", this is for the front form page. -7. Create a file "./main.go", this is for handling backend file upload process. - -Your folder&file structure should look like this after the preparation: - -![folder&file structure](folder_structure.png) - -## Work with DropzoneJS - -Open file "./views/upload.html" and let us create a DropzoneJs form. - -Copy the content below to "./views/upload.html" and we will go through each line of code individually. - -```html - - - - - DropzoneJS Uploader - - - - - - - - - - - -
-
- - -
-
- - - -``` - -1. Include the CSS Stylesheet. -2. Include DropzoneJS JavaScript library. -3. Create an upload form with css class "dropzone" and "action" is the route path "/upload". Note that we did create an input filed for fallback mode. This is all handled by DropzoneJS library itself. All we need to do is assign css class "dropzone" to the form. By default, DropzoneJS will find all forms with class "dropzone" and automatically attach itself to it. - -## Work with Go - -Now you have come to Last part of the tutorial. In this section, we will store files sent from DropzoneJS to the "./public/uploads" folder. - -Open "main.go" and copy the code below: - -```go -// main.go - -package main - -import ( - "os" - "io" - "strings" - - "github.com/kataras/iris/v12" -) - -const uploadsDir = "./public/uploads/" - -func main() { - app := iris.New() - - // Register templates - app.RegisterView(iris.HTML("./views", ".html")) - - // Make the /public route path to statically serve the ./public/... contents - app.HandleDir("/public", iris.Dir("./public")) - - // Render the actual form - // GET: http://localhost:8080 - app.Get("/", func(ctx iris.Context) { - ctx.View("upload.html") - }) - - // Upload the file to the server - // POST: http://localhost:8080/upload - app.Post("/upload", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) { - // Get the file from the dropzone request - file, info, err := ctx.FormFile("file") - if err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.Application().Logger().Warnf("Error while uploading: %v", err.Error()) - return - } - - defer file.Close() - fname := info.Filename - - // Create a file with the same name - // assuming that you have a folder named 'uploads' - out, err := os.OpenFile(uploadsDir+fname, - os.O_WRONLY|os.O_CREATE, 0666) - - if err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.Application().Logger().Warnf("Error while preparing the new file: %v", err.Error()) - return - } - defer out.Close() - - io.Copy(out, file) - }) - - // Start the server at http://localhost:8080 - app.Listen(":8080") -} -``` - -1. Create a new Iris app. -2. Register and load templates from the "views" folder. -3. Make the "/public" route path to statically serve the ./public/... folder's contents -4. Create a route to serve the upload form. -5. Create a route to handle the POST form data from the DropzoneJS' form -6. Declare a variable for destination folder. -7. If file is sent to the page, store the file object to a temporary "file" variable. -8. Move uploaded file to destination based on the uploadsDir+uploaded file's name. - -### Running the server - -Open the terminal at the current project's folder and execute: - -```bash -$ go run main.go -Now listening on: http://localhost:8080 -Application started. Press CTRL+C to shut down. -``` - -Now go to browser, and navigate to http://localhost:8080, you should be able to see a page as below: - -![no files screenshot](no_files.png) -![with uploaded files screenshot](with_files.png) \ No newline at end of file diff --git a/_examples/dropzonejs/README_PART2.md b/_examples/dropzonejs/README_PART2.md deleted file mode 100644 index e120e32aa4..0000000000 --- a/_examples/dropzonejs/README_PART2.md +++ /dev/null @@ -1,310 +0,0 @@ -# Articles - -* [How to build a file upload form using DropzoneJS and Go](https://hackernoon.com/how-to-build-a-file-upload-form-using-dropzonejs-and-go-8fb9f258a991) -* [How to display existing files on server using DropzoneJS and Go](https://hackernoon.com/how-to-display-existing-files-on-server-using-dropzonejs-and-go-53e24b57ba19) - -# Content - -This is the part 2 of 2 in DropzoneJS + Go series. - -- [Part 1: How to build a file upload form](README.md) -- [Part 2: How to display existing files on server](README_PART2.md) - -# DropzoneJS + Go: How to display existing files on server - -In this tutorial, we will show you how to display existing files on the server when using DropzoneJS and Go. This tutorial is based on [How to build a file upload form using DropzoneJS and Go](README.md). Make sure you have read it before proceeding to content in this tutorial. - -## Table Of Content - -- [Preparation](#preparation) -- [Modify the Server side](#modify-the-server-side) -- [Modify the Client side](#modify-the-client-side) -- [References](#references) -- [The End](#the-end) - -## Preparation - -Install the go package "github.com/nfnt/resize" with `go get github.com/nfnt/resize`, we need it to create thumbnails. - -In previous [tutorial](README.md). We have already set up a proper working DropzoneJs upload form. There is no additional file needed for this tutorial. What we need to do is to make some modifications to file below: - -1. main.go -2. views/upload.html - -Let us get started! - -## Modify the Server side - -In previous tutorial. All "/upload" does is to store uploaded files to the server directory "./public/uploads". So we need to add a piece of code to retrieve stored files' information (name and size), and return it in JSON format. - -Copy the content below to "main.go". Read comments for details. - -```go -// main.go - -package main - -import ( - "image/jpeg" - "image/png" - "io" - "os" - "path" - "path/filepath" - "strings" - "sync" - - "github.com/kataras/iris/v12" - - "github.com/nfnt/resize" // $ go get -u github.com/nfnt/resize -) - -const uploadsDir = "./public/uploads/" - -type uploadedFile struct { - // {name: "", size: } are the dropzone's only requirements. - Name string `json:"name"` - Size int64 `json:"size"` -} - -type uploadedFiles struct { - dir string - items []uploadedFile - mu sync.RWMutex // slices are safe but RWMutex is a good practise for you. -} - -// scan the ./public/uploads folder for any files -// add them to a new uploadedFiles list. -func scanUploads(dir string) *uploadedFiles { - f := new(uploadedFiles) - - lindex := dir[len(dir)-1] - if lindex != os.PathSeparator && lindex != '/' { - dir += string(os.PathSeparator) - } - - // create directories if necessary - // and if, then return empty uploaded files; skipping the scan. - if err := os.MkdirAll(dir, os.FileMode(0666)); err != nil { - return f - } - - // otherwise scan the given "dir" for files. - f.scan(dir) - return f -} - -func (f *uploadedFiles) scan(dir string) { - f.dir = dir - filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - - // if it's directory or a thumbnail we saved earlier, skip it. - if info.IsDir() || strings.HasPrefix(info.Name(), "thumbnail_") { - return nil - } - - f.add(info.Name(), info.Size()) - return nil - }) -} - -// add the file's Name and Size to the uploadedFiles memory list -func (f *uploadedFiles) add(name string, size int64) uploadedFile { - uf := uploadedFile{ - Name: name, - Size: size, - } - f.mu.Lock() - f.items = append(f.items, uf) - f.mu.Unlock() - - return uf -} - -// create thumbnail 100x100 -// and save that to the ./public/uploads/thumbnail_$FILENAME -func (f *uploadedFiles) createThumbnail(uf uploadedFile) { - file, err := os.Open(path.Join(f.dir, uf.Name)) - if err != nil { - return - } - defer file.Close() - - name := strings.ToLower(uf.Name) - - out, err := os.OpenFile(f.dir+"thumbnail_"+uf.Name, - os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - return - } - defer out.Close() - - if strings.HasSuffix(name, ".jpg") { - // decode jpeg into image.Image - img, err := jpeg.Decode(file) - if err != nil { - return - } - - // write new image to file - resized := resize.Thumbnail(180, 180, img, resize.Lanczos3) - jpeg.Encode(out, resized, - &jpeg.Options{Quality: jpeg.DefaultQuality}) - - } else if strings.HasSuffix(name, ".png") { - img, err := png.Decode(file) - if err != nil { - return - } - - // write new image to file - resized := resize.Thumbnail(180, 180, img, resize.Lanczos3) // slower but better res - png.Encode(out, resized) - } - // and so on... you got the point, this code can be simplify, as a practise. -} - -func main() { - app := iris.New() - app.RegisterView(iris.HTML("./views", ".html")) - - app.HandleDir("/public", iris.Dir("./public")) - - app.Get("/", func(ctx iris.Context) { - ctx.View("upload.html") - }) - - files := scanUploads(uploadsDir) - - app.Get("/uploads", func(ctx iris.Context) { - ctx.JSON(files.items) - }) - - app.Post("/upload", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) { - // Get the file from the dropzone request - file, info, err := ctx.FormFile("file") - if err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.Application().Logger().Warnf("Error while uploading: %v", err.Error()) - return - } - - defer file.Close() - fname := info.Filename - - // Create a file with the same name - // assuming that you have a folder named 'uploads' - out, err := os.OpenFile(uploadsDir+fname, - os.O_WRONLY|os.O_CREATE, 0666) - - if err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.Application().Logger().Warnf("Error while preparing the new file: %v", err.Error()) - return - } - defer out.Close() - - io.Copy(out, file) - - // optionally, add that file to the list in order to be visible when refresh. - uploadedFile := files.add(fname, info.Size) - go files.createThumbnail(uploadedFile) - }) - - // start the server at http://localhost:8080 - app.Listen(":8080") -} -``` - -## Modify the Client side - -Copy content below to "./views/upload.html". We will go through modifications individually. - -```html - - - - - DropzoneJS Uploader - - - - - - - - - - - - - - - -
-
- - -
-
- - - -``` - -1. We added Jquery library into our page. This actually not for DropzoneJs directly. We are using Jquery's ajax function **$.get** only. You will see below -2. We added an ID element (my-dropzone) to the form. This is needed because we need to pass configuration values to Dropzone. And to do it, we must have an ID reference of it. So that we can configure it by assigning values to Dropzone.options.myDropzone. A lot of people face confusion when configuring Dropzone. To put it in a simple way. Do not take Dropzone as a Jquery plugin, it has its own syntax and you need to follow it. -3. This starts the main part of modification. What we did here is to pass a function to listen to Dropzone's init event. This event is called when Dropzone is initialized. -4. Retrieve files details from the new "/uploads" via ajax. -5. Create mockFile using values from server. mockFile is simply JavaScript objects with properties of name and size. Then we call Dropzone's **addedfile** and **thumbnail** functions explicitly to put existing files to Dropzone upload area and generate its thumbnail. - -### Running the server - -Open the terminal at the current project's folder and execute: - -```bash -$ go run main.go -Now listening on: http://localhost:8080 -Application started. Press CTRL+C to shut down. -``` - -If you have done it successfully. Now go and upload some images and reload the upload page. Already uploaded files should auto display in Dropzone area. - -![with uploaded files screenshot](with_files.png) - -## References - -- http://www.dropzonejs.com/#server-side-implementation -- https://www.startutorial.com/articles/view/how-to-build-a-file-upload-form-using-dropzonejs-and-php -- https://docs.iris-go.com -- https://github.com/kataras/iris/tree/master/_examples/dropzonejs - -## The end - -Hopefully this simple tutorial helped you with your development. -If you like my post, please follow me on [Twitter](https://twitter.com/makismaropoulos) and help spread the word. I need your support to continue. \ No newline at end of file diff --git a/_examples/dropzonejs/folder_structure.png b/_examples/dropzonejs/folder_structure.png deleted file mode 100644 index 2c81637c34..0000000000 Binary files a/_examples/dropzonejs/folder_structure.png and /dev/null differ diff --git a/_examples/dropzonejs/meta.yml b/_examples/dropzonejs/meta.yml deleted file mode 100644 index 8b23e94800..0000000000 --- a/_examples/dropzonejs/meta.yml +++ /dev/null @@ -1,8 +0,0 @@ -Name: DropzoneJS -Articles: - - Title: How to build a file upload form using DropzoneJS and Go - Source: https://medium.com/hackernoon/how-to-build-a-file-upload-form-using-dropzonejs-and-go-8fb9f258a991 - Author: https://twitter.com/@kataras - - Title: How to display existing files on server using DropzoneJS and Go - Source: https://medium.com/@kataras/how-to-display-existing-files-on-server-using-dropzonejs-and-go-53e24b57ba19 - Author: https://twitter.com/@kataras \ No newline at end of file diff --git a/_examples/dropzonejs/no_files.png b/_examples/dropzonejs/no_files.png deleted file mode 100644 index 6e4afacb52..0000000000 Binary files a/_examples/dropzonejs/no_files.png and /dev/null differ diff --git a/_examples/dropzonejs/src/main.go b/_examples/dropzonejs/src/main.go deleted file mode 100644 index edc97f8597..0000000000 --- a/_examples/dropzonejs/src/main.go +++ /dev/null @@ -1,168 +0,0 @@ -package main - -import ( - "image/jpeg" - "image/png" - "io" - "os" - "path" - "path/filepath" - "strings" - "sync" - - "github.com/kataras/iris/v12" - - "github.com/nfnt/resize" -) - -// $ go get -u github.com/nfnt/resize - -const uploadsDir = "./public/uploads/" - -type uploadedFile struct { - // {name: "", size: } are the dropzone's only requirements. - Name string `json:"name"` - Size int64 `json:"size"` -} - -type uploadedFiles struct { - dir string - items []uploadedFile - mu sync.RWMutex // slices are safe but RWMutex is a good practise for you. -} - -func scanUploads(dir string) *uploadedFiles { - f := new(uploadedFiles) - - lindex := dir[len(dir)-1] - if lindex != os.PathSeparator && lindex != '/' { - dir += string(os.PathSeparator) - } - - // create directories if necessary - // and if, then return empty uploaded files; skipping the scan. - if err := os.MkdirAll(dir, os.FileMode(0666)); err != nil { - return f - } - - // otherwise scan the given "dir" for files. - f.scan(dir) - return f -} - -func (f *uploadedFiles) scan(dir string) { - f.dir = dir - filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - // if it's directory or a thumbnail we saved earlier, skip it. - if info.IsDir() || strings.HasPrefix(info.Name(), "thumbnail_") { - return nil - } - - f.add(info.Name(), info.Size()) - return nil - }) -} - -func (f *uploadedFiles) add(name string, size int64) uploadedFile { - uf := uploadedFile{ - Name: name, - Size: size, - } - - f.mu.Lock() - f.items = append(f.items, uf) - f.mu.Unlock() - - return uf -} - -func (f *uploadedFiles) createThumbnail(uf uploadedFile) { - file, err := os.Open(path.Join(f.dir, uf.Name)) - if err != nil { - return - } - defer file.Close() - - name := strings.ToLower(uf.Name) - - out, err := os.OpenFile(f.dir+"thumbnail_"+uf.Name, - os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - return - } - defer out.Close() - - if strings.HasSuffix(name, ".jpg") { - // decode jpeg into image.Image - img, err := jpeg.Decode(file) - if err != nil { - return - } - - // write new image to file - resized := resize.Thumbnail(180, 180, img, resize.Lanczos3) - jpeg.Encode(out, resized, - &jpeg.Options{Quality: jpeg.DefaultQuality}) - - } else if strings.HasSuffix(name, ".png") { - img, err := png.Decode(file) - if err != nil { - return - } - - // write new image to file - resized := resize.Thumbnail(180, 180, img, resize.Lanczos3) // slower but better res - png.Encode(out, resized) - } - // and so on... you got the point, this code can be simplify, as a practise. -} - -func main() { - app := iris.New() - app.RegisterView(iris.HTML("./views", ".html")) - - app.HandleDir("/public", iris.Dir("./public")) - - app.Get("/", func(ctx iris.Context) { - ctx.View("upload.html") - }) - - files := scanUploads(uploadsDir) - - app.Get("/uploads", func(ctx iris.Context) { - ctx.JSON(files.items) - }) - - app.Post("/upload", iris.LimitRequestBodySize(10<<20), func(ctx iris.Context) { - // Get the file from the dropzone request - file, info, err := ctx.FormFile("file") - if err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.Application().Logger().Warnf("Error while uploading: %v", err.Error()) - return - } - - defer file.Close() - fname := info.Filename - - // Create a file with the same name - // assuming that you have a folder named 'uploads' - out, err := os.OpenFile(uploadsDir+fname, - os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.Application().Logger().Warnf("Error while preparing the new file: %v", err.Error()) - return - } - defer out.Close() - - io.Copy(out, file) - - // optionally, add that file to the list in order to be visible when refresh. - uploadedFile := files.add(fname, info.Size) - go files.createThumbnail(uploadedFile) - }) - - // start the server at http://localhost:8080 - app.Listen(":8080") -} diff --git a/_examples/dropzonejs/src/public/css/dropzone.css b/_examples/dropzonejs/src/public/css/dropzone.css deleted file mode 100644 index 0494d1ccf4..0000000000 --- a/_examples/dropzonejs/src/public/css/dropzone.css +++ /dev/null @@ -1,388 +0,0 @@ -/* - * The MIT License - * Copyright (c) 2012 Matias Meno - */ -@-webkit-keyframes passing-through { - 0% { - opacity: 0; - -webkit-transform: translateY(40px); - -moz-transform: translateY(40px); - -ms-transform: translateY(40px); - -o-transform: translateY(40px); - transform: translateY(40px); } - 30%, 70% { - opacity: 1; - -webkit-transform: translateY(0px); - -moz-transform: translateY(0px); - -ms-transform: translateY(0px); - -o-transform: translateY(0px); - transform: translateY(0px); } - 100% { - opacity: 0; - -webkit-transform: translateY(-40px); - -moz-transform: translateY(-40px); - -ms-transform: translateY(-40px); - -o-transform: translateY(-40px); - transform: translateY(-40px); } } -@-moz-keyframes passing-through { - 0% { - opacity: 0; - -webkit-transform: translateY(40px); - -moz-transform: translateY(40px); - -ms-transform: translateY(40px); - -o-transform: translateY(40px); - transform: translateY(40px); } - 30%, 70% { - opacity: 1; - -webkit-transform: translateY(0px); - -moz-transform: translateY(0px); - -ms-transform: translateY(0px); - -o-transform: translateY(0px); - transform: translateY(0px); } - 100% { - opacity: 0; - -webkit-transform: translateY(-40px); - -moz-transform: translateY(-40px); - -ms-transform: translateY(-40px); - -o-transform: translateY(-40px); - transform: translateY(-40px); } } -@keyframes passing-through { - 0% { - opacity: 0; - -webkit-transform: translateY(40px); - -moz-transform: translateY(40px); - -ms-transform: translateY(40px); - -o-transform: translateY(40px); - transform: translateY(40px); } - 30%, 70% { - opacity: 1; - -webkit-transform: translateY(0px); - -moz-transform: translateY(0px); - -ms-transform: translateY(0px); - -o-transform: translateY(0px); - transform: translateY(0px); } - 100% { - opacity: 0; - -webkit-transform: translateY(-40px); - -moz-transform: translateY(-40px); - -ms-transform: translateY(-40px); - -o-transform: translateY(-40px); - transform: translateY(-40px); } } -@-webkit-keyframes slide-in { - 0% { - opacity: 0; - -webkit-transform: translateY(40px); - -moz-transform: translateY(40px); - -ms-transform: translateY(40px); - -o-transform: translateY(40px); - transform: translateY(40px); } - 30% { - opacity: 1; - -webkit-transform: translateY(0px); - -moz-transform: translateY(0px); - -ms-transform: translateY(0px); - -o-transform: translateY(0px); - transform: translateY(0px); } } -@-moz-keyframes slide-in { - 0% { - opacity: 0; - -webkit-transform: translateY(40px); - -moz-transform: translateY(40px); - -ms-transform: translateY(40px); - -o-transform: translateY(40px); - transform: translateY(40px); } - 30% { - opacity: 1; - -webkit-transform: translateY(0px); - -moz-transform: translateY(0px); - -ms-transform: translateY(0px); - -o-transform: translateY(0px); - transform: translateY(0px); } } -@keyframes slide-in { - 0% { - opacity: 0; - -webkit-transform: translateY(40px); - -moz-transform: translateY(40px); - -ms-transform: translateY(40px); - -o-transform: translateY(40px); - transform: translateY(40px); } - 30% { - opacity: 1; - -webkit-transform: translateY(0px); - -moz-transform: translateY(0px); - -ms-transform: translateY(0px); - -o-transform: translateY(0px); - transform: translateY(0px); } } -@-webkit-keyframes pulse { - 0% { - -webkit-transform: scale(1); - -moz-transform: scale(1); - -ms-transform: scale(1); - -o-transform: scale(1); - transform: scale(1); } - 10% { - -webkit-transform: scale(1.1); - -moz-transform: scale(1.1); - -ms-transform: scale(1.1); - -o-transform: scale(1.1); - transform: scale(1.1); } - 20% { - -webkit-transform: scale(1); - -moz-transform: scale(1); - -ms-transform: scale(1); - -o-transform: scale(1); - transform: scale(1); } } -@-moz-keyframes pulse { - 0% { - -webkit-transform: scale(1); - -moz-transform: scale(1); - -ms-transform: scale(1); - -o-transform: scale(1); - transform: scale(1); } - 10% { - -webkit-transform: scale(1.1); - -moz-transform: scale(1.1); - -ms-transform: scale(1.1); - -o-transform: scale(1.1); - transform: scale(1.1); } - 20% { - -webkit-transform: scale(1); - -moz-transform: scale(1); - -ms-transform: scale(1); - -o-transform: scale(1); - transform: scale(1); } } -@keyframes pulse { - 0% { - -webkit-transform: scale(1); - -moz-transform: scale(1); - -ms-transform: scale(1); - -o-transform: scale(1); - transform: scale(1); } - 10% { - -webkit-transform: scale(1.1); - -moz-transform: scale(1.1); - -ms-transform: scale(1.1); - -o-transform: scale(1.1); - transform: scale(1.1); } - 20% { - -webkit-transform: scale(1); - -moz-transform: scale(1); - -ms-transform: scale(1); - -o-transform: scale(1); - transform: scale(1); } } -.dropzone, .dropzone * { - box-sizing: border-box; } - -.dropzone { - min-height: 150px; - border: 2px solid rgba(0, 0, 0, 0.3); - background: white; - padding: 20px 20px; } - .dropzone.dz-clickable { - cursor: pointer; } - .dropzone.dz-clickable * { - cursor: default; } - .dropzone.dz-clickable .dz-message, .dropzone.dz-clickable .dz-message * { - cursor: pointer; } - .dropzone.dz-started .dz-message { - display: none; } - .dropzone.dz-drag-hover { - border-style: solid; } - .dropzone.dz-drag-hover .dz-message { - opacity: 0.5; } - .dropzone .dz-message { - text-align: center; - margin: 2em 0; } - .dropzone .dz-preview { - position: relative; - display: inline-block; - vertical-align: top; - margin: 16px; - min-height: 100px; } - .dropzone .dz-preview:hover { - z-index: 1000; } - .dropzone .dz-preview:hover .dz-details { - opacity: 1; } - .dropzone .dz-preview.dz-file-preview .dz-image { - border-radius: 20px; - background: #999; - background: linear-gradient(to bottom, #eee, #ddd); } - .dropzone .dz-preview.dz-file-preview .dz-details { - opacity: 1; } - .dropzone .dz-preview.dz-image-preview { - background: white; } - .dropzone .dz-preview.dz-image-preview .dz-details { - -webkit-transition: opacity 0.2s linear; - -moz-transition: opacity 0.2s linear; - -ms-transition: opacity 0.2s linear; - -o-transition: opacity 0.2s linear; - transition: opacity 0.2s linear; } - .dropzone .dz-preview .dz-remove { - font-size: 14px; - text-align: center; - display: block; - cursor: pointer; - border: none; } - .dropzone .dz-preview .dz-remove:hover { - text-decoration: underline; } - .dropzone .dz-preview:hover .dz-details { - opacity: 1; } - .dropzone .dz-preview .dz-details { - z-index: 20; - position: absolute; - top: 0; - left: 0; - opacity: 0; - font-size: 13px; - min-width: 100%; - max-width: 100%; - padding: 2em 1em; - text-align: center; - color: rgba(0, 0, 0, 0.9); - line-height: 150%; } - .dropzone .dz-preview .dz-details .dz-size { - margin-bottom: 1em; - font-size: 16px; } - .dropzone .dz-preview .dz-details .dz-filename { - white-space: nowrap; } - .dropzone .dz-preview .dz-details .dz-filename:hover span { - border: 1px solid rgba(200, 200, 200, 0.8); - background-color: rgba(255, 255, 255, 0.8); } - .dropzone .dz-preview .dz-details .dz-filename:not(:hover) { - overflow: hidden; - text-overflow: ellipsis; } - .dropzone .dz-preview .dz-details .dz-filename:not(:hover) span { - border: 1px solid transparent; } - .dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span { - background-color: rgba(255, 255, 255, 0.4); - padding: 0 0.4em; - border-radius: 3px; } - .dropzone .dz-preview:hover .dz-image img { - -webkit-transform: scale(1.05, 1.05); - -moz-transform: scale(1.05, 1.05); - -ms-transform: scale(1.05, 1.05); - -o-transform: scale(1.05, 1.05); - transform: scale(1.05, 1.05); - -webkit-filter: blur(8px); - filter: blur(8px); } - .dropzone .dz-preview .dz-image { - border-radius: 20px; - overflow: hidden; - width: 120px; - height: 120px; - position: relative; - display: block; - z-index: 10; } - .dropzone .dz-preview .dz-image img { - display: block; } - .dropzone .dz-preview.dz-success .dz-success-mark { - -webkit-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); - -moz-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); - -ms-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); - -o-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); - animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); } - .dropzone .dz-preview.dz-error .dz-error-mark { - opacity: 1; - -webkit-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); - -moz-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); - -ms-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); - -o-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); - animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); } - .dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark { - pointer-events: none; - opacity: 0; - z-index: 500; - position: absolute; - display: block; - top: 50%; - left: 50%; - margin-left: -27px; - margin-top: -27px; } - .dropzone .dz-preview .dz-success-mark svg, .dropzone .dz-preview .dz-error-mark svg { - display: block; - width: 54px; - height: 54px; } - .dropzone .dz-preview.dz-processing .dz-progress { - opacity: 1; - -webkit-transition: all 0.2s linear; - -moz-transition: all 0.2s linear; - -ms-transition: all 0.2s linear; - -o-transition: all 0.2s linear; - transition: all 0.2s linear; } - .dropzone .dz-preview.dz-complete .dz-progress { - opacity: 0; - -webkit-transition: opacity 0.4s ease-in; - -moz-transition: opacity 0.4s ease-in; - -ms-transition: opacity 0.4s ease-in; - -o-transition: opacity 0.4s ease-in; - transition: opacity 0.4s ease-in; } - .dropzone .dz-preview:not(.dz-processing) .dz-progress { - -webkit-animation: pulse 6s ease infinite; - -moz-animation: pulse 6s ease infinite; - -ms-animation: pulse 6s ease infinite; - -o-animation: pulse 6s ease infinite; - animation: pulse 6s ease infinite; } - .dropzone .dz-preview .dz-progress { - opacity: 1; - z-index: 1000; - pointer-events: none; - position: absolute; - height: 16px; - left: 50%; - top: 50%; - margin-top: -8px; - width: 80px; - margin-left: -40px; - background: rgba(255, 255, 255, 0.9); - -webkit-transform: scale(1); - border-radius: 8px; - overflow: hidden; } - .dropzone .dz-preview .dz-progress .dz-upload { - background: #333; - background: linear-gradient(to bottom, #666, #444); - position: absolute; - top: 0; - left: 0; - bottom: 0; - width: 0; - -webkit-transition: width 300ms ease-in-out; - -moz-transition: width 300ms ease-in-out; - -ms-transition: width 300ms ease-in-out; - -o-transition: width 300ms ease-in-out; - transition: width 300ms ease-in-out; } - .dropzone .dz-preview.dz-error .dz-error-message { - display: block; } - .dropzone .dz-preview.dz-error:hover .dz-error-message { - opacity: 1; - pointer-events: auto; } - .dropzone .dz-preview .dz-error-message { - pointer-events: none; - z-index: 1000; - position: absolute; - display: block; - display: none; - opacity: 0; - -webkit-transition: opacity 0.3s ease; - -moz-transition: opacity 0.3s ease; - -ms-transition: opacity 0.3s ease; - -o-transition: opacity 0.3s ease; - transition: opacity 0.3s ease; - border-radius: 8px; - font-size: 13px; - top: 130px; - left: -10px; - width: 140px; - background: #be2626; - background: linear-gradient(to bottom, #be2626, #a92222); - padding: 0.5em 1.2em; - color: white; } - .dropzone .dz-preview .dz-error-message:after { - content: ''; - position: absolute; - top: -6px; - left: 64px; - width: 0; - height: 0; - border-left: 6px solid transparent; - border-right: 6px solid transparent; - border-bottom: 6px solid #be2626; } diff --git a/_examples/dropzonejs/src/public/js/dropzone.js b/_examples/dropzonejs/src/public/js/dropzone.js deleted file mode 100644 index 1bf9a7fe47..0000000000 --- a/_examples/dropzonejs/src/public/js/dropzone.js +++ /dev/null @@ -1,2052 +0,0 @@ - -/* - * - * More info at [www.dropzonejs.com](http://www.dropzonejs.com) - * - * Copyright (c) 2012, Matias Meno - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -(function() { - var Dropzone, Emitter, ExifRestore, camelize, contentLoaded, detectVerticalSquash, drawImageIOSFix, noop, without, - slice = [].slice, - extend1 = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, - hasProp = {}.hasOwnProperty; - - noop = function() {}; - - Emitter = (function() { - function Emitter() {} - - Emitter.prototype.addEventListener = Emitter.prototype.on; - - Emitter.prototype.on = function(event, fn) { - this._callbacks = this._callbacks || {}; - if (!this._callbacks[event]) { - this._callbacks[event] = []; - } - this._callbacks[event].push(fn); - return this; - }; - - Emitter.prototype.emit = function() { - var args, callback, callbacks, event, j, len; - event = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; - this._callbacks = this._callbacks || {}; - callbacks = this._callbacks[event]; - if (callbacks) { - for (j = 0, len = callbacks.length; j < len; j++) { - callback = callbacks[j]; - callback.apply(this, args); - } - } - return this; - }; - - Emitter.prototype.removeListener = Emitter.prototype.off; - - Emitter.prototype.removeAllListeners = Emitter.prototype.off; - - Emitter.prototype.removeEventListener = Emitter.prototype.off; - - Emitter.prototype.off = function(event, fn) { - var callback, callbacks, i, j, len; - if (!this._callbacks || arguments.length === 0) { - this._callbacks = {}; - return this; - } - callbacks = this._callbacks[event]; - if (!callbacks) { - return this; - } - if (arguments.length === 1) { - delete this._callbacks[event]; - return this; - } - for (i = j = 0, len = callbacks.length; j < len; i = ++j) { - callback = callbacks[i]; - if (callback === fn) { - callbacks.splice(i, 1); - break; - } - } - return this; - }; - - return Emitter; - - })(); - - Dropzone = (function(superClass) { - var extend, resolveOption; - - extend1(Dropzone, superClass); - - Dropzone.prototype.Emitter = Emitter; - - - /* - This is a list of all available events you can register on a dropzone object. - - You can register an event handler like this: - - dropzone.on("dragEnter", function() { }); - */ - - Dropzone.prototype.events = ["drop", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "addedfile", "addedfiles", "removedfile", "thumbnail", "error", "errormultiple", "processing", "processingmultiple", "uploadprogress", "totaluploadprogress", "sending", "sendingmultiple", "success", "successmultiple", "canceled", "canceledmultiple", "complete", "completemultiple", "reset", "maxfilesexceeded", "maxfilesreached", "queuecomplete"]; - - Dropzone.prototype.defaultOptions = { - url: null, - method: "post", - withCredentials: false, - timeout: 30000, - parallelUploads: 2, - uploadMultiple: false, - maxFilesize: 256, - paramName: "file", - createImageThumbnails: true, - maxThumbnailFilesize: 10, - thumbnailWidth: 120, - thumbnailHeight: 120, - thumbnailMethod: 'crop', - resizeWidth: null, - resizeHeight: null, - resizeMimeType: null, - resizeQuality: 0.8, - resizeMethod: 'contain', - filesizeBase: 1000, - maxFiles: null, - params: {}, - headers: null, - clickable: true, - ignoreHiddenFiles: true, - acceptedFiles: null, - acceptedMimeTypes: null, - autoProcessQueue: true, - autoQueue: true, - addRemoveLinks: false, - previewsContainer: null, - hiddenInputContainer: "body", - capture: null, - renameFilename: null, - renameFile: null, - forceFallback: false, - dictDefaultMessage: "Drop files here to upload", - dictFallbackMessage: "Your browser does not support drag'n'drop file uploads.", - dictFallbackText: "Please use the fallback form below to upload your files like in the olden days.", - dictFileTooBig: "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.", - dictInvalidFileType: "You can't upload files of this type.", - dictResponseError: "Server responded with {{statusCode}} code.", - dictCancelUpload: "Cancel upload", - dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?", - dictRemoveFile: "Remove file", - dictRemoveFileConfirmation: null, - dictMaxFilesExceeded: "You can not upload any more files.", - dictFileSizeUnits: { - tb: "TB", - gb: "GB", - mb: "MB", - kb: "KB", - b: "b" - }, - init: function() { - return noop; - }, - accept: function(file, done) { - return done(); - }, - fallback: function() { - var child, j, len, messageElement, ref, span; - this.element.className = this.element.className + " dz-browser-not-supported"; - ref = this.element.getElementsByTagName("div"); - for (j = 0, len = ref.length; j < len; j++) { - child = ref[j]; - if (/(^| )dz-message($| )/.test(child.className)) { - messageElement = child; - child.className = "dz-message"; - continue; - } - } - if (!messageElement) { - messageElement = Dropzone.createElement("
"); - this.element.appendChild(messageElement); - } - span = messageElement.getElementsByTagName("span")[0]; - if (span) { - if (span.textContent != null) { - span.textContent = this.options.dictFallbackMessage; - } else if (span.innerText != null) { - span.innerText = this.options.dictFallbackMessage; - } - } - return this.element.appendChild(this.getFallbackForm()); - }, - resize: function(file, width, height, resizeMethod) { - var info, srcRatio, trgRatio; - info = { - srcX: 0, - srcY: 0, - srcWidth: file.width, - srcHeight: file.height - }; - srcRatio = file.width / file.height; - if ((width == null) && (height == null)) { - width = info.srcWidth; - height = info.srcHeight; - } else if (width == null) { - width = height * srcRatio; - } else if (height == null) { - height = width / srcRatio; - } - width = Math.min(width, info.srcWidth); - height = Math.min(height, info.srcHeight); - trgRatio = width / height; - if (info.srcWidth > width || info.srcHeight > height) { - if (resizeMethod === 'crop') { - if (srcRatio > trgRatio) { - info.srcHeight = file.height; - info.srcWidth = info.srcHeight * trgRatio; - } else { - info.srcWidth = file.width; - info.srcHeight = info.srcWidth / trgRatio; - } - } else if (resizeMethod === 'contain') { - if (srcRatio > trgRatio) { - height = width / srcRatio; - } else { - width = height * srcRatio; - } - } else { - throw new Error("Unknown resizeMethod '" + resizeMethod + "'"); - } - } - info.srcX = (file.width - info.srcWidth) / 2; - info.srcY = (file.height - info.srcHeight) / 2; - info.trgWidth = width; - info.trgHeight = height; - return info; - }, - transformFile: function(file, done) { - if ((this.options.resizeWidth || this.options.resizeHeight) && file.type.match(/image.*/)) { - return this.resizeImage(file, this.options.resizeWidth, this.options.resizeHeight, this.options.resizeMethod, done); - } else { - return done(file); - } - }, - previewTemplate: "
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n \n
\n
", - - /* - Those functions register themselves to the events on init and handle all - the user interface specific stuff. Overwriting them won't break the upload - but can break the way it's displayed. - You can overwrite them if you don't like the default behavior. If you just - want to add an additional event handler, register it on the dropzone object - and don't overwrite those options. - */ - drop: function(e) { - return this.element.classList.remove("dz-drag-hover"); - }, - dragstart: noop, - dragend: function(e) { - return this.element.classList.remove("dz-drag-hover"); - }, - dragenter: function(e) { - return this.element.classList.add("dz-drag-hover"); - }, - dragover: function(e) { - return this.element.classList.add("dz-drag-hover"); - }, - dragleave: function(e) { - return this.element.classList.remove("dz-drag-hover"); - }, - paste: noop, - reset: function() { - return this.element.classList.remove("dz-started"); - }, - addedfile: function(file) { - var j, k, l, len, len1, len2, node, ref, ref1, ref2, removeFileEvent, removeLink, results; - if (this.element === this.previewsContainer) { - this.element.classList.add("dz-started"); - } - if (this.previewsContainer) { - file.previewElement = Dropzone.createElement(this.options.previewTemplate.trim()); - file.previewTemplate = file.previewElement; - this.previewsContainer.appendChild(file.previewElement); - ref = file.previewElement.querySelectorAll("[data-dz-name]"); - for (j = 0, len = ref.length; j < len; j++) { - node = ref[j]; - node.textContent = file.name; - } - ref1 = file.previewElement.querySelectorAll("[data-dz-size]"); - for (k = 0, len1 = ref1.length; k < len1; k++) { - node = ref1[k]; - node.innerHTML = this.filesize(file.size); - } - if (this.options.addRemoveLinks) { - file._removeLink = Dropzone.createElement("" + this.options.dictRemoveFile + ""); - file.previewElement.appendChild(file._removeLink); - } - removeFileEvent = (function(_this) { - return function(e) { - e.preventDefault(); - e.stopPropagation(); - if (file.status === Dropzone.UPLOADING) { - return Dropzone.confirm(_this.options.dictCancelUploadConfirmation, function() { - return _this.removeFile(file); - }); - } else { - if (_this.options.dictRemoveFileConfirmation) { - return Dropzone.confirm(_this.options.dictRemoveFileConfirmation, function() { - return _this.removeFile(file); - }); - } else { - return _this.removeFile(file); - } - } - }; - })(this); - ref2 = file.previewElement.querySelectorAll("[data-dz-remove]"); - results = []; - for (l = 0, len2 = ref2.length; l < len2; l++) { - removeLink = ref2[l]; - results.push(removeLink.addEventListener("click", removeFileEvent)); - } - return results; - } - }, - removedfile: function(file) { - var ref; - if (file.previewElement) { - if ((ref = file.previewElement) != null) { - ref.parentNode.removeChild(file.previewElement); - } - } - return this._updateMaxFilesReachedClass(); - }, - thumbnail: function(file, dataUrl) { - var j, len, ref, thumbnailElement; - if (file.previewElement) { - file.previewElement.classList.remove("dz-file-preview"); - ref = file.previewElement.querySelectorAll("[data-dz-thumbnail]"); - for (j = 0, len = ref.length; j < len; j++) { - thumbnailElement = ref[j]; - thumbnailElement.alt = file.name; - thumbnailElement.src = dataUrl; - } - return setTimeout(((function(_this) { - return function() { - return file.previewElement.classList.add("dz-image-preview"); - }; - })(this)), 1); - } - }, - error: function(file, message) { - var j, len, node, ref, results; - if (file.previewElement) { - file.previewElement.classList.add("dz-error"); - if (typeof message !== "String" && message.error) { - message = message.error; - } - ref = file.previewElement.querySelectorAll("[data-dz-errormessage]"); - results = []; - for (j = 0, len = ref.length; j < len; j++) { - node = ref[j]; - results.push(node.textContent = message); - } - return results; - } - }, - errormultiple: noop, - processing: function(file) { - if (file.previewElement) { - file.previewElement.classList.add("dz-processing"); - if (file._removeLink) { - return file._removeLink.textContent = this.options.dictCancelUpload; - } - } - }, - processingmultiple: noop, - uploadprogress: function(file, progress, bytesSent) { - var j, len, node, ref, results; - if (file.previewElement) { - ref = file.previewElement.querySelectorAll("[data-dz-uploadprogress]"); - results = []; - for (j = 0, len = ref.length; j < len; j++) { - node = ref[j]; - if (node.nodeName === 'PROGRESS') { - results.push(node.value = progress); - } else { - results.push(node.style.width = progress + "%"); - } - } - return results; - } - }, - totaluploadprogress: noop, - sending: noop, - sendingmultiple: noop, - success: function(file) { - if (file.previewElement) { - return file.previewElement.classList.add("dz-success"); - } - }, - successmultiple: noop, - canceled: function(file) { - return this.emit("error", file, "Upload canceled."); - }, - canceledmultiple: noop, - complete: function(file) { - if (file._removeLink) { - file._removeLink.textContent = this.options.dictRemoveFile; - } - if (file.previewElement) { - return file.previewElement.classList.add("dz-complete"); - } - }, - completemultiple: noop, - maxfilesexceeded: noop, - maxfilesreached: noop, - queuecomplete: noop, - addedfiles: noop - }; - - extend = function() { - var j, key, len, object, objects, target, val; - target = arguments[0], objects = 2 <= arguments.length ? slice.call(arguments, 1) : []; - for (j = 0, len = objects.length; j < len; j++) { - object = objects[j]; - for (key in object) { - val = object[key]; - target[key] = val; - } - } - return target; - }; - - function Dropzone(element1, options) { - var elementOptions, fallback, ref; - this.element = element1; - this.version = Dropzone.version; - this.defaultOptions.previewTemplate = this.defaultOptions.previewTemplate.replace(/\n*/g, ""); - this.clickableElements = []; - this.listeners = []; - this.files = []; - if (typeof this.element === "string") { - this.element = document.querySelector(this.element); - } - if (!(this.element && (this.element.nodeType != null))) { - throw new Error("Invalid dropzone element."); - } - if (this.element.dropzone) { - throw new Error("Dropzone already attached."); - } - Dropzone.instances.push(this); - this.element.dropzone = this; - elementOptions = (ref = Dropzone.optionsForElement(this.element)) != null ? ref : {}; - this.options = extend({}, this.defaultOptions, elementOptions, options != null ? options : {}); - if (this.options.forceFallback || !Dropzone.isBrowserSupported()) { - return this.options.fallback.call(this); - } - if (this.options.url == null) { - this.options.url = this.element.getAttribute("action"); - } - if (!this.options.url) { - throw new Error("No URL provided."); - } - if (this.options.acceptedFiles && this.options.acceptedMimeTypes) { - throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated."); - } - if (this.options.acceptedMimeTypes) { - this.options.acceptedFiles = this.options.acceptedMimeTypes; - delete this.options.acceptedMimeTypes; - } - if (this.options.renameFilename != null) { - this.options.renameFile = (function(_this) { - return function(file) { - return _this.options.renameFilename.call(_this, file.name, file); - }; - })(this); - } - this.options.method = this.options.method.toUpperCase(); - if ((fallback = this.getExistingFallback()) && fallback.parentNode) { - fallback.parentNode.removeChild(fallback); - } - if (this.options.previewsContainer !== false) { - if (this.options.previewsContainer) { - this.previewsContainer = Dropzone.getElement(this.options.previewsContainer, "previewsContainer"); - } else { - this.previewsContainer = this.element; - } - } - if (this.options.clickable) { - if (this.options.clickable === true) { - this.clickableElements = [this.element]; - } else { - this.clickableElements = Dropzone.getElements(this.options.clickable, "clickable"); - } - } - this.init(); - } - - Dropzone.prototype.getAcceptedFiles = function() { - var file, j, len, ref, results; - ref = this.files; - results = []; - for (j = 0, len = ref.length; j < len; j++) { - file = ref[j]; - if (file.accepted) { - results.push(file); - } - } - return results; - }; - - Dropzone.prototype.getRejectedFiles = function() { - var file, j, len, ref, results; - ref = this.files; - results = []; - for (j = 0, len = ref.length; j < len; j++) { - file = ref[j]; - if (!file.accepted) { - results.push(file); - } - } - return results; - }; - - Dropzone.prototype.getFilesWithStatus = function(status) { - var file, j, len, ref, results; - ref = this.files; - results = []; - for (j = 0, len = ref.length; j < len; j++) { - file = ref[j]; - if (file.status === status) { - results.push(file); - } - } - return results; - }; - - Dropzone.prototype.getQueuedFiles = function() { - return this.getFilesWithStatus(Dropzone.QUEUED); - }; - - Dropzone.prototype.getUploadingFiles = function() { - return this.getFilesWithStatus(Dropzone.UPLOADING); - }; - - Dropzone.prototype.getAddedFiles = function() { - return this.getFilesWithStatus(Dropzone.ADDED); - }; - - Dropzone.prototype.getActiveFiles = function() { - var file, j, len, ref, results; - ref = this.files; - results = []; - for (j = 0, len = ref.length; j < len; j++) { - file = ref[j]; - if (file.status === Dropzone.UPLOADING || file.status === Dropzone.QUEUED) { - results.push(file); - } - } - return results; - }; - - Dropzone.prototype.init = function() { - var eventName, j, len, noPropagation, ref, ref1, setupHiddenFileInput; - if (this.element.tagName === "form") { - this.element.setAttribute("enctype", "multipart/form-data"); - } - if (this.element.classList.contains("dropzone") && !this.element.querySelector(".dz-message")) { - this.element.appendChild(Dropzone.createElement("
" + this.options.dictDefaultMessage + "
")); - } - if (this.clickableElements.length) { - setupHiddenFileInput = (function(_this) { - return function() { - if (_this.hiddenFileInput) { - _this.hiddenFileInput.parentNode.removeChild(_this.hiddenFileInput); - } - _this.hiddenFileInput = document.createElement("input"); - _this.hiddenFileInput.setAttribute("type", "file"); - if ((_this.options.maxFiles == null) || _this.options.maxFiles > 1) { - _this.hiddenFileInput.setAttribute("multiple", "multiple"); - } - _this.hiddenFileInput.className = "dz-hidden-input"; - if (_this.options.acceptedFiles != null) { - _this.hiddenFileInput.setAttribute("accept", _this.options.acceptedFiles); - } - if (_this.options.capture != null) { - _this.hiddenFileInput.setAttribute("capture", _this.options.capture); - } - _this.hiddenFileInput.style.visibility = "hidden"; - _this.hiddenFileInput.style.position = "absolute"; - _this.hiddenFileInput.style.top = "0"; - _this.hiddenFileInput.style.left = "0"; - _this.hiddenFileInput.style.height = "0"; - _this.hiddenFileInput.style.width = "0"; - document.querySelector(_this.options.hiddenInputContainer).appendChild(_this.hiddenFileInput); - return _this.hiddenFileInput.addEventListener("change", function() { - var file, files, j, len; - files = _this.hiddenFileInput.files; - if (files.length) { - for (j = 0, len = files.length; j < len; j++) { - file = files[j]; - _this.addFile(file); - } - } - _this.emit("addedfiles", files); - return setupHiddenFileInput(); - }); - }; - })(this); - setupHiddenFileInput(); - } - this.URL = (ref = window.URL) != null ? ref : window.webkitURL; - ref1 = this.events; - for (j = 0, len = ref1.length; j < len; j++) { - eventName = ref1[j]; - this.on(eventName, this.options[eventName]); - } - this.on("uploadprogress", (function(_this) { - return function() { - return _this.updateTotalUploadProgress(); - }; - })(this)); - this.on("removedfile", (function(_this) { - return function() { - return _this.updateTotalUploadProgress(); - }; - })(this)); - this.on("canceled", (function(_this) { - return function(file) { - return _this.emit("complete", file); - }; - })(this)); - this.on("complete", (function(_this) { - return function(file) { - if (_this.getAddedFiles().length === 0 && _this.getUploadingFiles().length === 0 && _this.getQueuedFiles().length === 0) { - return setTimeout((function() { - return _this.emit("queuecomplete"); - }), 0); - } - }; - })(this)); - noPropagation = function(e) { - e.stopPropagation(); - if (e.preventDefault) { - return e.preventDefault(); - } else { - return e.returnValue = false; - } - }; - this.listeners = [ - { - element: this.element, - events: { - "dragstart": (function(_this) { - return function(e) { - return _this.emit("dragstart", e); - }; - })(this), - "dragenter": (function(_this) { - return function(e) { - noPropagation(e); - return _this.emit("dragenter", e); - }; - })(this), - "dragover": (function(_this) { - return function(e) { - var efct; - try { - efct = e.dataTransfer.effectAllowed; - } catch (undefined) {} - e.dataTransfer.dropEffect = 'move' === efct || 'linkMove' === efct ? 'move' : 'copy'; - noPropagation(e); - return _this.emit("dragover", e); - }; - })(this), - "dragleave": (function(_this) { - return function(e) { - return _this.emit("dragleave", e); - }; - })(this), - "drop": (function(_this) { - return function(e) { - noPropagation(e); - return _this.drop(e); - }; - })(this), - "dragend": (function(_this) { - return function(e) { - return _this.emit("dragend", e); - }; - })(this) - } - } - ]; - this.clickableElements.forEach((function(_this) { - return function(clickableElement) { - return _this.listeners.push({ - element: clickableElement, - events: { - "click": function(evt) { - if ((clickableElement !== _this.element) || (evt.target === _this.element || Dropzone.elementInside(evt.target, _this.element.querySelector(".dz-message")))) { - _this.hiddenFileInput.click(); - } - return true; - } - } - }); - }; - })(this)); - this.enable(); - return this.options.init.call(this); - }; - - Dropzone.prototype.destroy = function() { - var ref; - this.disable(); - this.removeAllFiles(true); - if ((ref = this.hiddenFileInput) != null ? ref.parentNode : void 0) { - this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput); - this.hiddenFileInput = null; - } - delete this.element.dropzone; - return Dropzone.instances.splice(Dropzone.instances.indexOf(this), 1); - }; - - Dropzone.prototype.updateTotalUploadProgress = function() { - var activeFiles, file, j, len, ref, totalBytes, totalBytesSent, totalUploadProgress; - totalBytesSent = 0; - totalBytes = 0; - activeFiles = this.getActiveFiles(); - if (activeFiles.length) { - ref = this.getActiveFiles(); - for (j = 0, len = ref.length; j < len; j++) { - file = ref[j]; - totalBytesSent += file.upload.bytesSent; - totalBytes += file.upload.total; - } - totalUploadProgress = 100 * totalBytesSent / totalBytes; - } else { - totalUploadProgress = 100; - } - return this.emit("totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent); - }; - - Dropzone.prototype._getParamName = function(n) { - if (typeof this.options.paramName === "function") { - return this.options.paramName(n); - } else { - return "" + this.options.paramName + (this.options.uploadMultiple ? "[" + n + "]" : ""); - } - }; - - Dropzone.prototype._renameFile = function(file) { - if (typeof this.options.renameFile !== "function") { - return file.name; - } - return this.options.renameFile(file); - }; - - Dropzone.prototype.getFallbackForm = function() { - var existingFallback, fields, fieldsString, form; - if (existingFallback = this.getExistingFallback()) { - return existingFallback; - } - fieldsString = "
"; - if (this.options.dictFallbackText) { - fieldsString += "

" + this.options.dictFallbackText + "

"; - } - fieldsString += "
"; - fields = Dropzone.createElement(fieldsString); - if (this.element.tagName !== "FORM") { - form = Dropzone.createElement("
"); - form.appendChild(fields); - } else { - this.element.setAttribute("enctype", "multipart/form-data"); - this.element.setAttribute("method", this.options.method); - } - return form != null ? form : fields; - }; - - Dropzone.prototype.getExistingFallback = function() { - var fallback, getFallback, j, len, ref, tagName; - getFallback = function(elements) { - var el, j, len; - for (j = 0, len = elements.length; j < len; j++) { - el = elements[j]; - if (/(^| )fallback($| )/.test(el.className)) { - return el; - } - } - }; - ref = ["div", "form"]; - for (j = 0, len = ref.length; j < len; j++) { - tagName = ref[j]; - if (fallback = getFallback(this.element.getElementsByTagName(tagName))) { - return fallback; - } - } - }; - - Dropzone.prototype.setupEventListeners = function() { - var elementListeners, event, j, len, listener, ref, results; - ref = this.listeners; - results = []; - for (j = 0, len = ref.length; j < len; j++) { - elementListeners = ref[j]; - results.push((function() { - var ref1, results1; - ref1 = elementListeners.events; - results1 = []; - for (event in ref1) { - listener = ref1[event]; - results1.push(elementListeners.element.addEventListener(event, listener, false)); - } - return results1; - })()); - } - return results; - }; - - Dropzone.prototype.removeEventListeners = function() { - var elementListeners, event, j, len, listener, ref, results; - ref = this.listeners; - results = []; - for (j = 0, len = ref.length; j < len; j++) { - elementListeners = ref[j]; - results.push((function() { - var ref1, results1; - ref1 = elementListeners.events; - results1 = []; - for (event in ref1) { - listener = ref1[event]; - results1.push(elementListeners.element.removeEventListener(event, listener, false)); - } - return results1; - })()); - } - return results; - }; - - Dropzone.prototype.disable = function() { - var file, j, len, ref, results; - this.clickableElements.forEach(function(element) { - return element.classList.remove("dz-clickable"); - }); - this.removeEventListeners(); - ref = this.files; - results = []; - for (j = 0, len = ref.length; j < len; j++) { - file = ref[j]; - results.push(this.cancelUpload(file)); - } - return results; - }; - - Dropzone.prototype.enable = function() { - this.clickableElements.forEach(function(element) { - return element.classList.add("dz-clickable"); - }); - return this.setupEventListeners(); - }; - - Dropzone.prototype.filesize = function(size) { - var cutoff, i, j, len, selectedSize, selectedUnit, unit, units; - selectedSize = 0; - selectedUnit = "b"; - if (size > 0) { - units = ['tb', 'gb', 'mb', 'kb', 'b']; - for (i = j = 0, len = units.length; j < len; i = ++j) { - unit = units[i]; - cutoff = Math.pow(this.options.filesizeBase, 4 - i) / 10; - if (size >= cutoff) { - selectedSize = size / Math.pow(this.options.filesizeBase, 4 - i); - selectedUnit = unit; - break; - } - } - selectedSize = Math.round(10 * selectedSize) / 10; - } - return "" + selectedSize + " " + this.options.dictFileSizeUnits[selectedUnit]; - }; - - Dropzone.prototype._updateMaxFilesReachedClass = function() { - if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) { - if (this.getAcceptedFiles().length === this.options.maxFiles) { - this.emit('maxfilesreached', this.files); - } - return this.element.classList.add("dz-max-files-reached"); - } else { - return this.element.classList.remove("dz-max-files-reached"); - } - }; - - Dropzone.prototype.drop = function(e) { - var files, items; - if (!e.dataTransfer) { - return; - } - this.emit("drop", e); - files = e.dataTransfer.files; - this.emit("addedfiles", files); - if (files.length) { - items = e.dataTransfer.items; - if (items && items.length && (items[0].webkitGetAsEntry != null)) { - this._addFilesFromItems(items); - } else { - this.handleFiles(files); - } - } - }; - - Dropzone.prototype.paste = function(e) { - var items, ref; - if ((e != null ? (ref = e.clipboardData) != null ? ref.items : void 0 : void 0) == null) { - return; - } - this.emit("paste", e); - items = e.clipboardData.items; - if (items.length) { - return this._addFilesFromItems(items); - } - }; - - Dropzone.prototype.handleFiles = function(files) { - var file, j, len, results; - results = []; - for (j = 0, len = files.length; j < len; j++) { - file = files[j]; - results.push(this.addFile(file)); - } - return results; - }; - - Dropzone.prototype._addFilesFromItems = function(items) { - var entry, item, j, len, results; - results = []; - for (j = 0, len = items.length; j < len; j++) { - item = items[j]; - if ((item.webkitGetAsEntry != null) && (entry = item.webkitGetAsEntry())) { - if (entry.isFile) { - results.push(this.addFile(item.getAsFile())); - } else if (entry.isDirectory) { - results.push(this._addFilesFromDirectory(entry, entry.name)); - } else { - results.push(void 0); - } - } else if (item.getAsFile != null) { - if ((item.kind == null) || item.kind === "file") { - results.push(this.addFile(item.getAsFile())); - } else { - results.push(void 0); - } - } else { - results.push(void 0); - } - } - return results; - }; - - Dropzone.prototype._addFilesFromDirectory = function(directory, path) { - var dirReader, errorHandler, readEntries; - dirReader = directory.createReader(); - errorHandler = function(error) { - return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log(error) : void 0 : void 0; - }; - readEntries = (function(_this) { - return function() { - return dirReader.readEntries(function(entries) { - var entry, j, len; - if (entries.length > 0) { - for (j = 0, len = entries.length; j < len; j++) { - entry = entries[j]; - if (entry.isFile) { - entry.file(function(file) { - if (_this.options.ignoreHiddenFiles && file.name.substring(0, 1) === '.') { - return; - } - file.fullPath = path + "/" + file.name; - return _this.addFile(file); - }); - } else if (entry.isDirectory) { - _this._addFilesFromDirectory(entry, path + "/" + entry.name); - } - } - readEntries(); - } - return null; - }, errorHandler); - }; - })(this); - return readEntries(); - }; - - Dropzone.prototype.accept = function(file, done) { - if (file.size > this.options.maxFilesize * 1024 * 1024) { - return done(this.options.dictFileTooBig.replace("{{filesize}}", Math.round(file.size / 1024 / 10.24) / 100).replace("{{maxFilesize}}", this.options.maxFilesize)); - } else if (!Dropzone.isValidFile(file, this.options.acceptedFiles)) { - return done(this.options.dictInvalidFileType); - } else if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) { - done(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}", this.options.maxFiles)); - return this.emit("maxfilesexceeded", file); - } else { - return this.options.accept.call(this, file, done); - } - }; - - Dropzone.prototype.addFile = function(file) { - file.upload = { - progress: 0, - total: file.size, - bytesSent: 0, - filename: this._renameFile(file) - }; - this.files.push(file); - file.status = Dropzone.ADDED; - this.emit("addedfile", file); - this._enqueueThumbnail(file); - return this.accept(file, (function(_this) { - return function(error) { - if (error) { - file.accepted = false; - _this._errorProcessing([file], error); - } else { - file.accepted = true; - if (_this.options.autoQueue) { - _this.enqueueFile(file); - } - } - return _this._updateMaxFilesReachedClass(); - }; - })(this)); - }; - - Dropzone.prototype.enqueueFiles = function(files) { - var file, j, len; - for (j = 0, len = files.length; j < len; j++) { - file = files[j]; - this.enqueueFile(file); - } - return null; - }; - - Dropzone.prototype.enqueueFile = function(file) { - if (file.status === Dropzone.ADDED && file.accepted === true) { - file.status = Dropzone.QUEUED; - if (this.options.autoProcessQueue) { - return setTimeout(((function(_this) { - return function() { - return _this.processQueue(); - }; - })(this)), 0); - } - } else { - throw new Error("This file can't be queued because it has already been processed or was rejected."); - } - }; - - Dropzone.prototype._thumbnailQueue = []; - - Dropzone.prototype._processingThumbnail = false; - - Dropzone.prototype._enqueueThumbnail = function(file) { - if (this.options.createImageThumbnails && file.type.match(/image.*/) && file.size <= this.options.maxThumbnailFilesize * 1024 * 1024) { - this._thumbnailQueue.push(file); - return setTimeout(((function(_this) { - return function() { - return _this._processThumbnailQueue(); - }; - })(this)), 0); - } - }; - - Dropzone.prototype._processThumbnailQueue = function() { - var file; - if (this._processingThumbnail || this._thumbnailQueue.length === 0) { - return; - } - this._processingThumbnail = true; - file = this._thumbnailQueue.shift(); - return this.createThumbnail(file, this.options.thumbnailWidth, this.options.thumbnailHeight, this.options.thumbnailMethod, true, (function(_this) { - return function(dataUrl) { - _this.emit("thumbnail", file, dataUrl); - _this._processingThumbnail = false; - return _this._processThumbnailQueue(); - }; - })(this)); - }; - - Dropzone.prototype.removeFile = function(file) { - if (file.status === Dropzone.UPLOADING) { - this.cancelUpload(file); - } - this.files = without(this.files, file); - this.emit("removedfile", file); - if (this.files.length === 0) { - return this.emit("reset"); - } - }; - - Dropzone.prototype.removeAllFiles = function(cancelIfNecessary) { - var file, j, len, ref; - if (cancelIfNecessary == null) { - cancelIfNecessary = false; - } - ref = this.files.slice(); - for (j = 0, len = ref.length; j < len; j++) { - file = ref[j]; - if (file.status !== Dropzone.UPLOADING || cancelIfNecessary) { - this.removeFile(file); - } - } - return null; - }; - - Dropzone.prototype.resizeImage = function(file, width, height, resizeMethod, callback) { - return this.createThumbnail(file, width, height, resizeMethod, false, (function(_this) { - return function(dataUrl, canvas) { - var resizeMimeType, resizedDataURL; - if (canvas === null) { - return callback(file); - } else { - resizeMimeType = _this.options.resizeMimeType; - if (resizeMimeType == null) { - resizeMimeType = file.type; - } - resizedDataURL = canvas.toDataURL(resizeMimeType, _this.options.resizeQuality); - if (resizeMimeType === 'image/jpeg' || resizeMimeType === 'image/jpg') { - resizedDataURL = ExifRestore.restore(file.dataURL, resizedDataURL); - } - return callback(Dropzone.dataURItoBlob(resizedDataURL)); - } - }; - })(this)); - }; - - Dropzone.prototype.createThumbnail = function(file, width, height, resizeMethod, fixOrientation, callback) { - var fileReader; - fileReader = new FileReader; - fileReader.onload = (function(_this) { - return function() { - file.dataURL = fileReader.result; - if (file.type === "image/svg+xml") { - if (callback != null) { - callback(fileReader.result); - } - return; - } - return _this.createThumbnailFromUrl(file, width, height, resizeMethod, fixOrientation, callback); - }; - })(this); - return fileReader.readAsDataURL(file); - }; - - Dropzone.prototype.createThumbnailFromUrl = function(file, width, height, resizeMethod, fixOrientation, callback, crossOrigin) { - var img; - img = document.createElement("img"); - if (crossOrigin) { - img.crossOrigin = crossOrigin; - } - img.onload = (function(_this) { - return function() { - var loadExif; - loadExif = function(callback) { - return callback(1); - }; - if ((typeof EXIF !== "undefined" && EXIF !== null) && fixOrientation) { - loadExif = function(callback) { - return EXIF.getData(img, function() { - return callback(EXIF.getTag(this, 'Orientation')); - }); - }; - } - return loadExif(function(orientation) { - var canvas, ctx, ref, ref1, ref2, ref3, resizeInfo, thumbnail; - file.width = img.width; - file.height = img.height; - resizeInfo = _this.options.resize.call(_this, file, width, height, resizeMethod); - canvas = document.createElement("canvas"); - ctx = canvas.getContext("2d"); - canvas.width = resizeInfo.trgWidth; - canvas.height = resizeInfo.trgHeight; - if (orientation > 4) { - canvas.width = resizeInfo.trgHeight; - canvas.height = resizeInfo.trgWidth; - } - switch (orientation) { - case 2: - ctx.translate(canvas.width, 0); - ctx.scale(-1, 1); - break; - case 3: - ctx.translate(canvas.width, canvas.height); - ctx.rotate(Math.PI); - break; - case 4: - ctx.translate(0, canvas.height); - ctx.scale(1, -1); - break; - case 5: - ctx.rotate(0.5 * Math.PI); - ctx.scale(1, -1); - break; - case 6: - ctx.rotate(0.5 * Math.PI); - ctx.translate(0, -canvas.height); - break; - case 7: - ctx.rotate(0.5 * Math.PI); - ctx.translate(canvas.width, -canvas.height); - ctx.scale(-1, 1); - break; - case 8: - ctx.rotate(-0.5 * Math.PI); - ctx.translate(-canvas.width, 0); - } - drawImageIOSFix(ctx, img, (ref = resizeInfo.srcX) != null ? ref : 0, (ref1 = resizeInfo.srcY) != null ? ref1 : 0, resizeInfo.srcWidth, resizeInfo.srcHeight, (ref2 = resizeInfo.trgX) != null ? ref2 : 0, (ref3 = resizeInfo.trgY) != null ? ref3 : 0, resizeInfo.trgWidth, resizeInfo.trgHeight); - thumbnail = canvas.toDataURL("image/png"); - if (callback != null) { - return callback(thumbnail, canvas); - } - }); - }; - })(this); - if (callback != null) { - img.onerror = callback; - } - return img.src = file.dataURL; - }; - - Dropzone.prototype.processQueue = function() { - var i, parallelUploads, processingLength, queuedFiles; - parallelUploads = this.options.parallelUploads; - processingLength = this.getUploadingFiles().length; - i = processingLength; - if (processingLength >= parallelUploads) { - return; - } - queuedFiles = this.getQueuedFiles(); - if (!(queuedFiles.length > 0)) { - return; - } - if (this.options.uploadMultiple) { - return this.processFiles(queuedFiles.slice(0, parallelUploads - processingLength)); - } else { - while (i < parallelUploads) { - if (!queuedFiles.length) { - return; - } - this.processFile(queuedFiles.shift()); - i++; - } - } - }; - - Dropzone.prototype.processFile = function(file) { - return this.processFiles([file]); - }; - - Dropzone.prototype.processFiles = function(files) { - var file, j, len; - for (j = 0, len = files.length; j < len; j++) { - file = files[j]; - file.processing = true; - file.status = Dropzone.UPLOADING; - this.emit("processing", file); - } - if (this.options.uploadMultiple) { - this.emit("processingmultiple", files); - } - return this.uploadFiles(files); - }; - - Dropzone.prototype._getFilesWithXhr = function(xhr) { - var file, files; - return files = (function() { - var j, len, ref, results; - ref = this.files; - results = []; - for (j = 0, len = ref.length; j < len; j++) { - file = ref[j]; - if (file.xhr === xhr) { - results.push(file); - } - } - return results; - }).call(this); - }; - - Dropzone.prototype.cancelUpload = function(file) { - var groupedFile, groupedFiles, j, k, len, len1, ref; - if (file.status === Dropzone.UPLOADING) { - groupedFiles = this._getFilesWithXhr(file.xhr); - for (j = 0, len = groupedFiles.length; j < len; j++) { - groupedFile = groupedFiles[j]; - groupedFile.status = Dropzone.CANCELED; - } - file.xhr.abort(); - for (k = 0, len1 = groupedFiles.length; k < len1; k++) { - groupedFile = groupedFiles[k]; - this.emit("canceled", groupedFile); - } - if (this.options.uploadMultiple) { - this.emit("canceledmultiple", groupedFiles); - } - } else if ((ref = file.status) === Dropzone.ADDED || ref === Dropzone.QUEUED) { - file.status = Dropzone.CANCELED; - this.emit("canceled", file); - if (this.options.uploadMultiple) { - this.emit("canceledmultiple", [file]); - } - } - if (this.options.autoProcessQueue) { - return this.processQueue(); - } - }; - - resolveOption = function() { - var args, option; - option = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; - if (typeof option === 'function') { - return option.apply(this, args); - } - return option; - }; - - Dropzone.prototype.uploadFile = function(file) { - return this.uploadFiles([file]); - }; - - Dropzone.prototype.uploadFiles = function(files) { - var doneCounter, doneFunction, file, formData, handleError, headerName, headerValue, headers, i, input, inputName, inputType, j, k, key, l, len, len1, len2, len3, m, method, o, option, progressObj, ref, ref1, ref2, ref3, ref4, ref5, response, results, updateProgress, url, value, xhr; - xhr = new XMLHttpRequest(); - for (j = 0, len = files.length; j < len; j++) { - file = files[j]; - file.xhr = xhr; - } - method = resolveOption(this.options.method, files); - url = resolveOption(this.options.url, files); - xhr.open(method, url, true); - xhr.timeout = resolveOption(this.options.timeout, files); - xhr.withCredentials = !!this.options.withCredentials; - response = null; - handleError = (function(_this) { - return function() { - var k, len1, results; - results = []; - for (k = 0, len1 = files.length; k < len1; k++) { - file = files[k]; - results.push(_this._errorProcessing(files, response || _this.options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr)); - } - return results; - }; - })(this); - updateProgress = (function(_this) { - return function(e) { - var allFilesFinished, k, l, len1, len2, len3, m, progress, results; - if (e != null) { - progress = 100 * e.loaded / e.total; - for (k = 0, len1 = files.length; k < len1; k++) { - file = files[k]; - file.upload.progress = progress; - file.upload.total = e.total; - file.upload.bytesSent = e.loaded; - } - } else { - allFilesFinished = true; - progress = 100; - for (l = 0, len2 = files.length; l < len2; l++) { - file = files[l]; - if (!(file.upload.progress === 100 && file.upload.bytesSent === file.upload.total)) { - allFilesFinished = false; - } - file.upload.progress = progress; - file.upload.bytesSent = file.upload.total; - } - if (allFilesFinished) { - return; - } - } - results = []; - for (m = 0, len3 = files.length; m < len3; m++) { - file = files[m]; - results.push(_this.emit("uploadprogress", file, progress, file.upload.bytesSent)); - } - return results; - }; - })(this); - xhr.onload = (function(_this) { - return function(e) { - var error1, ref; - if (files[0].status === Dropzone.CANCELED) { - return; - } - if (xhr.readyState !== 4) { - return; - } - if (xhr.responseType !== 'arraybuffer' && xhr.responseType !== 'blob') { - response = xhr.responseText; - if (xhr.getResponseHeader("content-type") && ~xhr.getResponseHeader("content-type").indexOf("application/json")) { - try { - response = JSON.parse(response); - } catch (error1) { - e = error1; - response = "Invalid JSON response from server."; - } - } - } - updateProgress(); - if (!((200 <= (ref = xhr.status) && ref < 300))) { - return handleError(); - } else { - return _this._finished(files, response, e); - } - }; - })(this); - xhr.onerror = (function(_this) { - return function() { - if (files[0].status === Dropzone.CANCELED) { - return; - } - return handleError(); - }; - })(this); - progressObj = (ref = xhr.upload) != null ? ref : xhr; - progressObj.onprogress = updateProgress; - headers = { - "Accept": "application/json", - "Cache-Control": "no-cache", - "X-Requested-With": "XMLHttpRequest" - }; - if (this.options.headers) { - extend(headers, this.options.headers); - } - for (headerName in headers) { - headerValue = headers[headerName]; - if (headerValue) { - xhr.setRequestHeader(headerName, headerValue); - } - } - formData = new FormData(); - if (this.options.params) { - ref1 = this.options.params; - for (key in ref1) { - value = ref1[key]; - formData.append(key, value); - } - } - for (k = 0, len1 = files.length; k < len1; k++) { - file = files[k]; - this.emit("sending", file, xhr, formData); - } - if (this.options.uploadMultiple) { - this.emit("sendingmultiple", files, xhr, formData); - } - if (this.element.tagName === "FORM") { - ref2 = this.element.querySelectorAll("input, textarea, select, button"); - for (l = 0, len2 = ref2.length; l < len2; l++) { - input = ref2[l]; - inputName = input.getAttribute("name"); - inputType = input.getAttribute("type"); - if (input.tagName === "SELECT" && input.hasAttribute("multiple")) { - ref3 = input.options; - for (m = 0, len3 = ref3.length; m < len3; m++) { - option = ref3[m]; - if (option.selected) { - formData.append(inputName, option.value); - } - } - } else if (!inputType || ((ref4 = inputType.toLowerCase()) !== "checkbox" && ref4 !== "radio") || input.checked) { - formData.append(inputName, input.value); - } - } - } - doneCounter = 0; - results = []; - for (i = o = 0, ref5 = files.length - 1; 0 <= ref5 ? o <= ref5 : o >= ref5; i = 0 <= ref5 ? ++o : --o) { - doneFunction = (function(_this) { - return function(file, paramName, fileName) { - return function(transformedFile) { - formData.append(paramName, transformedFile, fileName); - if (++doneCounter === files.length) { - return _this.submitRequest(xhr, formData, files); - } - }; - }; - })(this); - results.push(this.options.transformFile.call(this, files[i], doneFunction(files[i], this._getParamName(i), files[i].upload.filename))); - } - return results; - }; - - Dropzone.prototype.submitRequest = function(xhr, formData, files) { - return xhr.send(formData); - }; - - Dropzone.prototype._finished = function(files, responseText, e) { - var file, j, len; - for (j = 0, len = files.length; j < len; j++) { - file = files[j]; - file.status = Dropzone.SUCCESS; - this.emit("success", file, responseText, e); - this.emit("complete", file); - } - if (this.options.uploadMultiple) { - this.emit("successmultiple", files, responseText, e); - this.emit("completemultiple", files); - } - if (this.options.autoProcessQueue) { - return this.processQueue(); - } - }; - - Dropzone.prototype._errorProcessing = function(files, message, xhr) { - var file, j, len; - for (j = 0, len = files.length; j < len; j++) { - file = files[j]; - file.status = Dropzone.ERROR; - this.emit("error", file, message, xhr); - this.emit("complete", file); - } - if (this.options.uploadMultiple) { - this.emit("errormultiple", files, message, xhr); - this.emit("completemultiple", files); - } - if (this.options.autoProcessQueue) { - return this.processQueue(); - } - }; - - return Dropzone; - - })(Emitter); - - Dropzone.version = "5.1.1"; - - Dropzone.options = {}; - - Dropzone.optionsForElement = function(element) { - if (element.getAttribute("id")) { - return Dropzone.options[camelize(element.getAttribute("id"))]; - } else { - return void 0; - } - }; - - Dropzone.instances = []; - - Dropzone.forElement = function(element) { - if (typeof element === "string") { - element = document.querySelector(element); - } - if ((element != null ? element.dropzone : void 0) == null) { - throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone."); - } - return element.dropzone; - }; - - Dropzone.autoDiscover = true; - - Dropzone.discover = function() { - var checkElements, dropzone, dropzones, j, len, results; - if (document.querySelectorAll) { - dropzones = document.querySelectorAll(".dropzone"); - } else { - dropzones = []; - checkElements = function(elements) { - var el, j, len, results; - results = []; - for (j = 0, len = elements.length; j < len; j++) { - el = elements[j]; - if (/(^| )dropzone($| )/.test(el.className)) { - results.push(dropzones.push(el)); - } else { - results.push(void 0); - } - } - return results; - }; - checkElements(document.getElementsByTagName("div")); - checkElements(document.getElementsByTagName("form")); - } - results = []; - for (j = 0, len = dropzones.length; j < len; j++) { - dropzone = dropzones[j]; - if (Dropzone.optionsForElement(dropzone) !== false) { - results.push(new Dropzone(dropzone)); - } else { - results.push(void 0); - } - } - return results; - }; - - Dropzone.blacklistedBrowsers = [/opera.*Macintosh.*version\/12/i]; - - Dropzone.isBrowserSupported = function() { - var capableBrowser, j, len, ref, regex; - capableBrowser = true; - if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) { - if (!("classList" in document.createElement("a"))) { - capableBrowser = false; - } else { - ref = Dropzone.blacklistedBrowsers; - for (j = 0, len = ref.length; j < len; j++) { - regex = ref[j]; - if (regex.test(navigator.userAgent)) { - capableBrowser = false; - continue; - } - } - } - } else { - capableBrowser = false; - } - return capableBrowser; - }; - - Dropzone.dataURItoBlob = function(dataURI) { - var ab, byteString, i, ia, j, mimeString, ref; - byteString = atob(dataURI.split(',')[1]); - mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; - ab = new ArrayBuffer(byteString.length); - ia = new Uint8Array(ab); - for (i = j = 0, ref = byteString.length; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) { - ia[i] = byteString.charCodeAt(i); - } - return new Blob([ab], { - type: mimeString - }); - }; - - without = function(list, rejectedItem) { - var item, j, len, results; - results = []; - for (j = 0, len = list.length; j < len; j++) { - item = list[j]; - if (item !== rejectedItem) { - results.push(item); - } - } - return results; - }; - - camelize = function(str) { - return str.replace(/[\-_](\w)/g, function(match) { - return match.charAt(1).toUpperCase(); - }); - }; - - Dropzone.createElement = function(string) { - var div; - div = document.createElement("div"); - div.innerHTML = string; - return div.childNodes[0]; - }; - - Dropzone.elementInside = function(element, container) { - if (element === container) { - return true; - } - while (element = element.parentNode) { - if (element === container) { - return true; - } - } - return false; - }; - - Dropzone.getElement = function(el, name) { - var element; - if (typeof el === "string") { - element = document.querySelector(el); - } else if (el.nodeType != null) { - element = el; - } - if (element == null) { - throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector or a plain HTML element."); - } - return element; - }; - - Dropzone.getElements = function(els, name) { - var e, el, elements, error1, j, k, len, len1, ref; - if (els instanceof Array) { - elements = []; - try { - for (j = 0, len = els.length; j < len; j++) { - el = els[j]; - elements.push(this.getElement(el, name)); - } - } catch (error1) { - e = error1; - elements = null; - } - } else if (typeof els === "string") { - elements = []; - ref = document.querySelectorAll(els); - for (k = 0, len1 = ref.length; k < len1; k++) { - el = ref[k]; - elements.push(el); - } - } else if (els.nodeType != null) { - elements = [els]; - } - if (!((elements != null) && elements.length)) { - throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector, a plain HTML element or a list of those."); - } - return elements; - }; - - Dropzone.confirm = function(question, accepted, rejected) { - if (window.confirm(question)) { - return accepted(); - } else if (rejected != null) { - return rejected(); - } - }; - - Dropzone.isValidFile = function(file, acceptedFiles) { - var baseMimeType, j, len, mimeType, validType; - if (!acceptedFiles) { - return true; - } - acceptedFiles = acceptedFiles.split(","); - mimeType = file.type; - baseMimeType = mimeType.replace(/\/.*$/, ""); - for (j = 0, len = acceptedFiles.length; j < len; j++) { - validType = acceptedFiles[j]; - validType = validType.trim(); - if (validType.charAt(0) === ".") { - if (file.name.toLowerCase().indexOf(validType.toLowerCase(), file.name.length - validType.length) !== -1) { - return true; - } - } else if (/\/\*$/.test(validType)) { - if (baseMimeType === validType.replace(/\/.*$/, "")) { - return true; - } - } else { - if (mimeType === validType) { - return true; - } - } - } - return false; - }; - - if (typeof jQuery !== "undefined" && jQuery !== null) { - jQuery.fn.dropzone = function(options) { - return this.each(function() { - return new Dropzone(this, options); - }); - }; - } - - if (typeof module !== "undefined" && module !== null) { - module.exports = Dropzone; - } else { - window.Dropzone = Dropzone; - } - - Dropzone.ADDED = "added"; - - Dropzone.QUEUED = "queued"; - - Dropzone.ACCEPTED = Dropzone.QUEUED; - - Dropzone.UPLOADING = "uploading"; - - Dropzone.PROCESSING = Dropzone.UPLOADING; - - Dropzone.CANCELED = "canceled"; - - Dropzone.ERROR = "error"; - - Dropzone.SUCCESS = "success"; - - - /* - - Bugfix for iOS 6 and 7 - Source: http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios - based on the work of https://github.com/stomita/ios-imagefile-megapixel - */ - - detectVerticalSquash = function(img) { - var alpha, canvas, ctx, data, ey, ih, iw, py, ratio, sy; - iw = img.naturalWidth; - ih = img.naturalHeight; - canvas = document.createElement("canvas"); - canvas.width = 1; - canvas.height = ih; - ctx = canvas.getContext("2d"); - ctx.drawImage(img, 0, 0); - data = ctx.getImageData(1, 0, 1, ih).data; - sy = 0; - ey = ih; - py = ih; - while (py > sy) { - alpha = data[(py - 1) * 4 + 3]; - if (alpha === 0) { - ey = py; - } else { - sy = py; - } - py = (ey + sy) >> 1; - } - ratio = py / ih; - if (ratio === 0) { - return 1; - } else { - return ratio; - } - }; - - drawImageIOSFix = function(ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) { - var vertSquashRatio; - vertSquashRatio = detectVerticalSquash(img); - return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio); - }; - - ExifRestore = (function() { - function ExifRestore() {} - - ExifRestore.KEY_STR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; - - ExifRestore.encode64 = function(input) { - var chr1, chr2, chr3, enc1, enc2, enc3, enc4, i, output; - output = ''; - chr1 = void 0; - chr2 = void 0; - chr3 = ''; - enc1 = void 0; - enc2 = void 0; - enc3 = void 0; - enc4 = ''; - i = 0; - while (true) { - chr1 = input[i++]; - chr2 = input[i++]; - chr3 = input[i++]; - enc1 = chr1 >> 2; - enc2 = (chr1 & 3) << 4 | chr2 >> 4; - enc3 = (chr2 & 15) << 2 | chr3 >> 6; - enc4 = chr3 & 63; - if (isNaN(chr2)) { - enc3 = enc4 = 64; - } else if (isNaN(chr3)) { - enc4 = 64; - } - output = output + this.KEY_STR.charAt(enc1) + this.KEY_STR.charAt(enc2) + this.KEY_STR.charAt(enc3) + this.KEY_STR.charAt(enc4); - chr1 = chr2 = chr3 = ''; - enc1 = enc2 = enc3 = enc4 = ''; - if (!(i < input.length)) { - break; - } - } - return output; - }; - - ExifRestore.restore = function(origFileBase64, resizedFileBase64) { - var image, rawImage, segments; - if (!origFileBase64.match('data:image/jpeg;base64,')) { - return resizedFileBase64; - } - rawImage = this.decode64(origFileBase64.replace('data:image/jpeg;base64,', '')); - segments = this.slice2Segments(rawImage); - image = this.exifManipulation(resizedFileBase64, segments); - return 'data:image/jpeg;base64,' + this.encode64(image); - }; - - ExifRestore.exifManipulation = function(resizedFileBase64, segments) { - var aBuffer, exifArray, newImageArray; - exifArray = this.getExifArray(segments); - newImageArray = this.insertExif(resizedFileBase64, exifArray); - aBuffer = new Uint8Array(newImageArray); - return aBuffer; - }; - - ExifRestore.getExifArray = function(segments) { - var seg, x; - seg = void 0; - x = 0; - while (x < segments.length) { - seg = segments[x]; - if (seg[0] === 255 & seg[1] === 225) { - return seg; - } - x++; - } - return []; - }; - - ExifRestore.insertExif = function(resizedFileBase64, exifArray) { - var array, ato, buf, imageData, mae, separatePoint; - imageData = resizedFileBase64.replace('data:image/jpeg;base64,', ''); - buf = this.decode64(imageData); - separatePoint = buf.indexOf(255, 3); - mae = buf.slice(0, separatePoint); - ato = buf.slice(separatePoint); - array = mae; - array = array.concat(exifArray); - array = array.concat(ato); - return array; - }; - - ExifRestore.slice2Segments = function(rawImageArray) { - var endPoint, head, length, seg, segments; - head = 0; - segments = []; - while (true) { - if (rawImageArray[head] === 255 & rawImageArray[head + 1] === 218) { - break; - } - if (rawImageArray[head] === 255 & rawImageArray[head + 1] === 216) { - head += 2; - } else { - length = rawImageArray[head + 2] * 256 + rawImageArray[head + 3]; - endPoint = head + length + 2; - seg = rawImageArray.slice(head, endPoint); - segments.push(seg); - head = endPoint; - } - if (head > rawImageArray.length) { - break; - } - } - return segments; - }; - - ExifRestore.decode64 = function(input) { - var base64test, buf, chr1, chr2, chr3, enc1, enc2, enc3, enc4, i, output; - output = ''; - chr1 = void 0; - chr2 = void 0; - chr3 = ''; - enc1 = void 0; - enc2 = void 0; - enc3 = void 0; - enc4 = ''; - i = 0; - buf = []; - base64test = /[^A-Za-z0-9\+\/\=]/g; - if (base64test.exec(input)) { - console.warning('There were invalid base64 characters in the input text.\n' + 'Valid base64 characters are A-Z, a-z, 0-9, \'+\', \'/\',and \'=\'\n' + 'Expect errors in decoding.'); - } - input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ''); - while (true) { - enc1 = this.KEY_STR.indexOf(input.charAt(i++)); - enc2 = this.KEY_STR.indexOf(input.charAt(i++)); - enc3 = this.KEY_STR.indexOf(input.charAt(i++)); - enc4 = this.KEY_STR.indexOf(input.charAt(i++)); - chr1 = enc1 << 2 | enc2 >> 4; - chr2 = (enc2 & 15) << 4 | enc3 >> 2; - chr3 = (enc3 & 3) << 6 | enc4; - buf.push(chr1); - if (enc3 !== 64) { - buf.push(chr2); - } - if (enc4 !== 64) { - buf.push(chr3); - } - chr1 = chr2 = chr3 = ''; - enc1 = enc2 = enc3 = enc4 = ''; - if (!(i < input.length)) { - break; - } - } - return buf; - }; - - return ExifRestore; - - })(); - - - /* - * contentloaded.js - * - * Author: Diego Perini (diego.perini at gmail.com) - * Summary: cross-browser wrapper for DOMContentLoaded - * Updated: 20101020 - * License: MIT - * Version: 1.2 - * - * URL: - * http://javascript.nwbox.com/ContentLoaded/ - * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE - */ - - contentLoaded = function(win, fn) { - var add, doc, done, init, poll, pre, rem, root, top; - done = false; - top = true; - doc = win.document; - root = doc.documentElement; - add = (doc.addEventListener ? "addEventListener" : "attachEvent"); - rem = (doc.addEventListener ? "removeEventListener" : "detachEvent"); - pre = (doc.addEventListener ? "" : "on"); - init = function(e) { - if (e.type === "readystatechange" && doc.readyState !== "complete") { - return; - } - (e.type === "load" ? win : doc)[rem](pre + e.type, init, false); - if (!done && (done = true)) { - return fn.call(win, e.type || e); - } - }; - poll = function() { - var e, error1; - try { - root.doScroll("left"); - } catch (error1) { - e = error1; - setTimeout(poll, 50); - return; - } - return init("poll"); - }; - if (doc.readyState !== "complete") { - if (doc.createEventObject && root.doScroll) { - try { - top = !win.frameElement; - } catch (undefined) {} - if (top) { - poll(); - } - } - doc[add](pre + "DOMContentLoaded", init, false); - doc[add](pre + "readystatechange", init, false); - return win[add](pre + "load", init, false); - } - }; - - Dropzone._autoDiscoverFunction = function() { - if (Dropzone.autoDiscover) { - return Dropzone.discover(); - } - }; - - contentLoaded(window, Dropzone._autoDiscoverFunction); - -}).call(this); diff --git a/_examples/dropzonejs/src/views/upload.html b/_examples/dropzonejs/src/views/upload.html deleted file mode 100644 index d7f0098719..0000000000 --- a/_examples/dropzonejs/src/views/upload.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - DropzoneJS Uploader - - - - - - - - - - - - - - - -
-
- - -
-
- - - \ No newline at end of file diff --git a/_examples/dropzonejs/with_files.png b/_examples/dropzonejs/with_files.png deleted file mode 100644 index 2b6f3cdc7b..0000000000 Binary files a/_examples/dropzonejs/with_files.png and /dev/null differ diff --git a/_examples/file-server/basic/assets.system/css/main.css b/_examples/file-server/basic/assets.system/css/main.css deleted file mode 100644 index 7db3df1d78..0000000000 --- a/_examples/file-server/basic/assets.system/css/main.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: black; -} diff --git a/_examples/file-server/basic/assets.system/test.txt b/_examples/file-server/basic/assets.system/test.txt deleted file mode 100644 index 1ce52e769a..0000000000 --- a/_examples/file-server/basic/assets.system/test.txt +++ /dev/null @@ -1 +0,0 @@ -main_test.go#TestHandleDirDot \ No newline at end of file diff --git a/_examples/file-server/basic/assets/app2/app22/just_a_text_no_index.txt b/_examples/file-server/basic/assets/app2/app22/just_a_text_no_index.txt deleted file mode 100644 index f1cc1278cf..0000000000 --- a/_examples/file-server/basic/assets/app2/app22/just_a_text_no_index.txt +++ /dev/null @@ -1 +0,0 @@ -just a text. \ No newline at end of file diff --git a/_examples/file-server/basic/assets/app2/app2app3/index.html b/_examples/file-server/basic/assets/app2/app2app3/index.html deleted file mode 100644 index 750f10bacb..0000000000 --- a/_examples/file-server/basic/assets/app2/app2app3/index.html +++ /dev/null @@ -1 +0,0 @@ -

Hello App2App3 index

\ No newline at end of file diff --git a/_examples/file-server/basic/assets/app2/index.html b/_examples/file-server/basic/assets/app2/index.html deleted file mode 100644 index 8193d82245..0000000000 --- a/_examples/file-server/basic/assets/app2/index.html +++ /dev/null @@ -1 +0,0 @@ -

Hello App2 index

\ No newline at end of file diff --git a/_examples/file-server/basic/assets/css/main.css b/_examples/file-server/basic/assets/css/main.css deleted file mode 100644 index fb72e54a97..0000000000 --- a/_examples/file-server/basic/assets/css/main.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: black; -} diff --git a/_examples/file-server/basic/assets/favicon.ico b/_examples/file-server/basic/assets/favicon.ico deleted file mode 100644 index c370da518e..0000000000 Binary files a/_examples/file-server/basic/assets/favicon.ico and /dev/null differ diff --git a/_examples/file-server/basic/assets/index.html b/_examples/file-server/basic/assets/index.html deleted file mode 100644 index 06651a4581..0000000000 --- a/_examples/file-server/basic/assets/index.html +++ /dev/null @@ -1 +0,0 @@ -

Hello index

\ No newline at end of file diff --git a/_examples/file-server/basic/assets/js/main.js b/_examples/file-server/basic/assets/js/main.js deleted file mode 100644 index 07f9351406..0000000000 --- a/_examples/file-server/basic/assets/js/main.js +++ /dev/null @@ -1 +0,0 @@ -console.log("example"); \ No newline at end of file diff --git a/_examples/file-server/basic/main.go b/_examples/file-server/basic/main.go deleted file mode 100644 index dc5fc10e0e..0000000000 --- a/_examples/file-server/basic/main.go +++ /dev/null @@ -1,66 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func newApp() *iris.Application { - app := iris.New() - - app.Favicon("./assets/favicon.ico") - - // first parameter is the request path - // second is the system directory - // - // app.HandleDir("/css", iris.Dir("./assets/css")) - // app.HandleDir("/js", iris.Dir("./assets/js")) - - v1 := app.Party("/v1") - v1.HandleDir("/static", iris.Dir("./assets"), iris.DirOptions{ - // Defaults to "/index.html", if request path is ending with **/*/$IndexName - // then it redirects to **/*(/) which another handler is handling it, - // that another handler, called index handler, is auto-registered by the framework - // if end developer does not managed to handle it by hand. - IndexName: "/index.html", - // When files should served under compression. - Compress: false, - // List the files inside the current requested directory if `IndexName` not found. - ShowList: false, - Cache: iris.DirCacheOptions{ - // enable in-memory cache and pre-compress the files. - Enable: true, - // ignore image types (and pdf). - CompressIgnore: iris.MatchImagesAssets, - // do not compress files smaller than size. - CompressMinSize: 300, - // available encodings that will be negotiated with client's needs. - Encodings: []string{"gzip", "br" /* you can also add: deflate, snappy */}, - }, - DirList: iris.DirListRich(), - // If `ShowList` is true then this function will be used instead of the default - // one to show the list of files of a current requested directory(dir). - // DirList: func(ctx iris.Context, dirName string, dir http.File) error { ... } - // - // Optional validator that loops through each requested resource. - // AssetValidator: func(ctx iris.Context, name string) bool { ... } - }) - - // You can also register any index handler manually, order of registration does not matter: - // v1.Get("/static", [...custom middleware...], func(ctx iris.Context) { - // [...custom code...] - // ctx.ServeFile("./assets/index.html") - // }) - - // http://localhost:8080/v1/static - // http://localhost:8080/v1/static/css/main.css - // http://localhost:8080/v1/static/js/jquery-2.1.1.js - // http://localhost:8080/v1/static/favicon.ico - return app -} - -func main() { - app := newApp() - app.Logger().SetLevel("debug") - - app.Listen(":8080") -} diff --git a/_examples/file-server/basic/main_test.go b/_examples/file-server/basic/main_test.go deleted file mode 100644 index 23db82293c..0000000000 --- a/_examples/file-server/basic/main_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package main - -import ( - "io/ioutil" - "path/filepath" - "strings" - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/httptest" -) - -type resource string - -func (r resource) contentType() string { - switch filepath.Ext(r.String()) { - case ".js": - return "text/javascript" - case ".css": - return "text/css" - case ".ico": - return "image/x-icon" - case ".html", "": - return "text/html" - default: - return "text/plain" - } -} - -func (r resource) String() string { - return string(r) -} - -func (r resource) strip(strip string) string { - s := r.String() - return strings.TrimPrefix(s, strip) -} - -func (r resource) loadFromBase(dir string, strip string) string { - filename := r.String() - - filename = r.strip(strip) - if filepath.Ext(filename) == "" { - // root /. - filename = filename + "/index.html" - } - - fullpath := filepath.Join(dir, filename) - - b, err := ioutil.ReadFile(fullpath) - if err != nil { - panic(fullpath + " failed with error: " + err.Error()) - } - - result := string(b) - - return result -} - -func TestFileServerBasic(t *testing.T) { - urls := []resource{ - "/v1/static/css/main.css", - "/v1/static/js/main.js", - "/v1/static/favicon.ico", - "/v1/static/app2", - "/v1/static/app2/app2app3", - "/v1/static", - } - - app := newApp() - // route := app.GetRouteReadOnly("GET/{file:path}") - // if route == nil { - // app.Logger().Fatalf("expected a route to serve files") - // } - - // if expected, got := "./assets", route.StaticDir(); expected != got { - // app.Logger().Fatalf("expected route's static directory to be: '%s' but got: '%s'", expected, got) - // } - - // if !route.StaticDirContainsIndex() { - // app.Logger().Fatalf("epxected ./assets to contain an %s file", "/index.html") - // } - - e := httptest.New(t, app) - for _, u := range urls { - url := u.String() - contents := u.loadFromBase("./assets", "/v1/static") - - e.GET(url).Expect(). - Status(httptest.StatusOK). - ContentType(u.contentType(), app.ConfigurationReadOnly().GetCharset()). - Body().Equal(contents) - } -} - -// Tests subdomain + request path and system directory with a name that contains a dot(.) -func TestHandleDirDot(t *testing.T) { - urls := []resource{ - "/v1/assets.system/css/main.css", - } - app := newApp() - app.Subdomain("test").Party("/v1").HandleDir("/assets.system", iris.Dir("./assets.system")) - - e := httptest.New(t, app, httptest.URL("http://test.example.com")) - for _, u := range urls { - url := u.String() - contents := u.loadFromBase("./assets.system", "/v1/assets.system") - - e.GET(url).Expect(). - Status(httptest.StatusOK). - ContentType(u.contentType(), app.ConfigurationReadOnly().GetCharset()). - Body().Equal(contents) - } -} diff --git a/_examples/file-server/embedding-files-into-app/assets/css/bootstrap.min.css b/_examples/file-server/embedding-files-into-app/assets/css/bootstrap.min.css deleted file mode 100644 index c2522313c1..0000000000 --- a/_examples/file-server/embedding-files-into-app/assets/css/bootstrap.min.css +++ /dev/null @@ -1,7225 +0,0 @@ -/*! - * Bootstrap v3.3.4 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ -html { - font-family: sans-serif; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100% -} - -body { - margin: 0 -} - -article, aside, details, figcaption, figure, footer, header, hgroup, - main, menu, nav, section, summary { - display: block -} - -audio, canvas, progress, video { - display: inline-block; - vertical-align: baseline -} - -audio:not ([controls] ){ - display: none; - height: 0 -} - -[hidden], template { - display: none -} - -a { - background-color: transparent -} - -a:active, a:hover { - outline: 0 -} - -abbr[title] { - border-bottom: 1px dotted -} - -b, strong { - font-weight: 700 -} - -dfn { - font-style: italic -} - -h1 { - margin: .67em 0; - font-size: 2em -} - -mark { - color: #000; - background: #ff0 -} - -small { - font-size: 80% -} - -sub, sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline -} - -sup { - top: -.5em -} - -sub { - bottom: -.25em -} - -img { - border: 0 -} - -svg:not (:root ){ - overflow: hidden -} - -figure { - margin: 1em 40px -} - -hr { - height: 0; - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box -} - -pre { - overflow: auto -} - -code, kbd, pre, samp { - font-family: monospace, monospace; - font-size: 1em -} - -button, input, optgroup, select, textarea { - margin: 0; - font: inherit; - color: inherit -} - -button { - overflow: visible -} - -button, select { - text-transform: none -} - -button, html input[type=button], input[type=reset], input[type=submit] { - -webkit-appearance: button; - cursor: pointer -} - -button[disabled], html input[disabled] { - cursor: default -} - -button::-moz-focus-inner, input::-moz-focus-inner { - padding: 0; - border: 0 -} - -input { - line-height: normal -} - -input[type=checkbox], input[type=radio] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - padding: 0 -} - -input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer-spin-button - { - height: auto -} - -input[type=search] { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; - -webkit-appearance: textfield -} - -input[type=search]::-webkit-search-cancel-button, input[type=search]::-webkit-search-decoration - { - -webkit-appearance: none -} - -fieldset { - padding: .35em .625em .75em; - margin: 0 2px; - border: 1px solid silver -} - -legend { - padding: 0; - border: 0 -} - -textarea { - overflow: auto -} - -optgroup { - font-weight: 700 -} - -table { - border-spacing: 0; - border-collapse: collapse -} - -td, th { - padding: 0 -} - /*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ -@media print { - *, :after, :before { - color: #000 !important; - text-shadow: none !important; - background: 0 0 !important; - -webkit-box-shadow: none !important; - box-shadow: none !important - } - a, a:visited { - text-decoration: underline - } - a[href]:after { - content: " (" attr(href) ")" - } - abbr[title]:after { - content: " (" attr(title) ")" - } - a[href^="javascript:"]:after, a[href^="#"]:after { - content: "" - } - blockquote, pre { - border: 1px solid #999; - page-break-inside: avoid - } - thead { - display: table-header-group - } - img, tr { - page-break-inside: avoid - } - img { - max-width: 100% !important - } - h2, h3, p { - orphans: 3; - widows: 3 - } - h2, h3 { - page-break-after: avoid - } - select { - background: #fff !important - } - .navbar { - display: none - } - .btn>.caret, .dropup>.btn>.caret { - border-top-color: #000 !important - } - .label { - border: 1px solid #000 - } - .table { - border-collapse: collapse !important - } - .table td, .table th { - background-color: #fff !important - } - .table-bordered td, .table-bordered th { - border: 1px solid #ddd !important - } -} - -@font-face { - font-family: 'Glyphicons Halflings'; - src: url(../fonts/glyphicons-halflings-regular.eot); - src: url(../fonts/glyphicons-halflings-regular.eot?#iefix) - format('embedded-opentype'), - url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'), - url(../fonts/glyphicons-halflings-regular.woff) format('woff'), - url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'), - url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) - format('svg') -} - -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: 400; - line-height: 1; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale -} - -.glyphicon-asterisk:before { - content: "\2a" -} - -.glyphicon-plus:before { - content: "\2b" -} - -.glyphicon-eur:before, .glyphicon-euro:before { - content: "\20ac" -} - -.glyphicon-minus:before { - content: "\2212" -} - -.glyphicon-cloud:before { - content: "\2601" -} - -.glyphicon-envelope:before { - content: "\2709" -} - -.glyphicon-pencil:before { - content: "\270f" -} - -.glyphicon-glass:before { - content: "\e001" -} - -.glyphicon-music:before { - content: "\e002" -} - -.glyphicon-search:before { - content: "\e003" -} - -.glyphicon-heart:before { - content: "\e005" -} - -.glyphicon-star:before { - content: "\e006" -} - -.glyphicon-star-empty:before { - content: "\e007" -} - -.glyphicon-user:before { - content: "\e008" -} - -.glyphicon-film:before { - content: "\e009" -} - -.glyphicon-th-large:before { - content: "\e010" -} - -.glyphicon-th:before { - content: "\e011" -} - -.glyphicon-th-list:before { - content: "\e012" -} - -.glyphicon-ok:before { - content: "\e013" -} - -.glyphicon-remove:before { - content: "\e014" -} - -.glyphicon-zoom-in:before { - content: "\e015" -} - -.glyphicon-zoom-out:before { - content: "\e016" -} - -.glyphicon-off:before { - content: "\e017" -} - -.glyphicon-signal:before { - content: "\e018" -} - -.glyphicon-cog:before { - content: "\e019" -} - -.glyphicon-trash:before { - content: "\e020" -} - -.glyphicon-home:before { - content: "\e021" -} - -.glyphicon-file:before { - content: "\e022" -} - -.glyphicon-time:before { - content: "\e023" -} - -.glyphicon-road:before { - content: "\e024" -} - -.glyphicon-download-alt:before { - content: "\e025" -} - -.glyphicon-download:before { - content: "\e026" -} - -.glyphicon-upload:before { - content: "\e027" -} - -.glyphicon-inbox:before { - content: "\e028" -} - -.glyphicon-play-circle:before { - content: "\e029" -} - -.glyphicon-repeat:before { - content: "\e030" -} - -.glyphicon-refresh:before { - content: "\e031" -} - -.glyphicon-list-alt:before { - content: "\e032" -} - -.glyphicon-lock:before { - content: "\e033" -} - -.glyphicon-flag:before { - content: "\e034" -} - -.glyphicon-headphones:before { - content: "\e035" -} - -.glyphicon-volume-off:before { - content: "\e036" -} - -.glyphicon-volume-down:before { - content: "\e037" -} - -.glyphicon-volume-up:before { - content: "\e038" -} - -.glyphicon-qrcode:before { - content: "\e039" -} - -.glyphicon-barcode:before { - content: "\e040" -} - -.glyphicon-tag:before { - content: "\e041" -} - -.glyphicon-tags:before { - content: "\e042" -} - -.glyphicon-book:before { - content: "\e043" -} - -.glyphicon-bookmark:before { - content: "\e044" -} - -.glyphicon-print:before { - content: "\e045" -} - -.glyphicon-camera:before { - content: "\e046" -} - -.glyphicon-font:before { - content: "\e047" -} - -.glyphicon-bold:before { - content: "\e048" -} - -.glyphicon-italic:before { - content: "\e049" -} - -.glyphicon-text-height:before { - content: "\e050" -} - -.glyphicon-text-width:before { - content: "\e051" -} - -.glyphicon-align-left:before { - content: "\e052" -} - -.glyphicon-align-center:before { - content: "\e053" -} - -.glyphicon-align-right:before { - content: "\e054" -} - -.glyphicon-align-justify:before { - content: "\e055" -} - -.glyphicon-list:before { - content: "\e056" -} - -.glyphicon-indent-left:before { - content: "\e057" -} - -.glyphicon-indent-right:before { - content: "\e058" -} - -.glyphicon-facetime-video:before { - content: "\e059" -} - -.glyphicon-picture:before { - content: "\e060" -} - -.glyphicon-map-marker:before { - content: "\e062" -} - -.glyphicon-adjust:before { - content: "\e063" -} - -.glyphicon-tint:before { - content: "\e064" -} - -.glyphicon-edit:before { - content: "\e065" -} - -.glyphicon-share:before { - content: "\e066" -} - -.glyphicon-check:before { - content: "\e067" -} - -.glyphicon-move:before { - content: "\e068" -} - -.glyphicon-step-backward:before { - content: "\e069" -} - -.glyphicon-fast-backward:before { - content: "\e070" -} - -.glyphicon-backward:before { - content: "\e071" -} - -.glyphicon-play:before { - content: "\e072" -} - -.glyphicon-pause:before { - content: "\e073" -} - -.glyphicon-stop:before { - content: "\e074" -} - -.glyphicon-forward:before { - content: "\e075" -} - -.glyphicon-fast-forward:before { - content: "\e076" -} - -.glyphicon-step-forward:before { - content: "\e077" -} - -.glyphicon-eject:before { - content: "\e078" -} - -.glyphicon-chevron-left:before { - content: "\e079" -} - -.glyphicon-chevron-right:before { - content: "\e080" -} - -.glyphicon-plus-sign:before { - content: "\e081" -} - -.glyphicon-minus-sign:before { - content: "\e082" -} - -.glyphicon-remove-sign:before { - content: "\e083" -} - -.glyphicon-ok-sign:before { - content: "\e084" -} - -.glyphicon-question-sign:before { - content: "\e085" -} - -.glyphicon-info-sign:before { - content: "\e086" -} - -.glyphicon-screenshot:before { - content: "\e087" -} - -.glyphicon-remove-circle:before { - content: "\e088" -} - -.glyphicon-ok-circle:before { - content: "\e089" -} - -.glyphicon-ban-circle:before { - content: "\e090" -} - -.glyphicon-arrow-left:before { - content: "\e091" -} - -.glyphicon-arrow-right:before { - content: "\e092" -} - -.glyphicon-arrow-up:before { - content: "\e093" -} - -.glyphicon-arrow-down:before { - content: "\e094" -} - -.glyphicon-share-alt:before { - content: "\e095" -} - -.glyphicon-resize-full:before { - content: "\e096" -} - -.glyphicon-resize-small:before { - content: "\e097" -} - -.glyphicon-exclamation-sign:before { - content: "\e101" -} - -.glyphicon-gift:before { - content: "\e102" -} - -.glyphicon-leaf:before { - content: "\e103" -} - -.glyphicon-fire:before { - content: "\e104" -} - -.glyphicon-eye-open:before { - content: "\e105" -} - -.glyphicon-eye-close:before { - content: "\e106" -} - -.glyphicon-warning-sign:before { - content: "\e107" -} - -.glyphicon-plane:before { - content: "\e108" -} - -.glyphicon-calendar:before { - content: "\e109" -} - -.glyphicon-random:before { - content: "\e110" -} - -.glyphicon-comment:before { - content: "\e111" -} - -.glyphicon-magnet:before { - content: "\e112" -} - -.glyphicon-chevron-up:before { - content: "\e113" -} - -.glyphicon-chevron-down:before { - content: "\e114" -} - -.glyphicon-retweet:before { - content: "\e115" -} - -.glyphicon-shopping-cart:before { - content: "\e116" -} - -.glyphicon-folder-close:before { - content: "\e117" -} - -.glyphicon-folder-open:before { - content: "\e118" -} - -.glyphicon-resize-vertical:before { - content: "\e119" -} - -.glyphicon-resize-horizontal:before { - content: "\e120" -} - -.glyphicon-hdd:before { - content: "\e121" -} - -.glyphicon-bullhorn:before { - content: "\e122" -} - -.glyphicon-bell:before { - content: "\e123" -} - -.glyphicon-certificate:before { - content: "\e124" -} - -.glyphicon-thumbs-up:before { - content: "\e125" -} - -.glyphicon-thumbs-down:before { - content: "\e126" -} - -.glyphicon-hand-right:before { - content: "\e127" -} - -.glyphicon-hand-left:before { - content: "\e128" -} - -.glyphicon-hand-up:before { - content: "\e129" -} - -.glyphicon-hand-down:before { - content: "\e130" -} - -.glyphicon-circle-arrow-right:before { - content: "\e131" -} - -.glyphicon-circle-arrow-left:before { - content: "\e132" -} - -.glyphicon-circle-arrow-up:before { - content: "\e133" -} - -.glyphicon-circle-arrow-down:before { - content: "\e134" -} - -.glyphicon-globe:before { - content: "\e135" -} - -.glyphicon-wrench:before { - content: "\e136" -} - -.glyphicon-tasks:before { - content: "\e137" -} - -.glyphicon-filter:before { - content: "\e138" -} - -.glyphicon-briefcase:before { - content: "\e139" -} - -.glyphicon-fullscreen:before { - content: "\e140" -} - -.glyphicon-dashboard:before { - content: "\e141" -} - -.glyphicon-paperclip:before { - content: "\e142" -} - -.glyphicon-heart-empty:before { - content: "\e143" -} - -.glyphicon-link:before { - content: "\e144" -} - -.glyphicon-phone:before { - content: "\e145" -} - -.glyphicon-pushpin:before { - content: "\e146" -} - -.glyphicon-usd:before { - content: "\e148" -} - -.glyphicon-gbp:before { - content: "\e149" -} - -.glyphicon-sort:before { - content: "\e150" -} - -.glyphicon-sort-by-alphabet:before { - content: "\e151" -} - -.glyphicon-sort-by-alphabet-alt:before { - content: "\e152" -} - -.glyphicon-sort-by-order:before { - content: "\e153" -} - -.glyphicon-sort-by-order-alt:before { - content: "\e154" -} - -.glyphicon-sort-by-attributes:before { - content: "\e155" -} - -.glyphicon-sort-by-attributes-alt:before { - content: "\e156" -} - -.glyphicon-unchecked:before { - content: "\e157" -} - -.glyphicon-expand:before { - content: "\e158" -} - -.glyphicon-collapse-down:before { - content: "\e159" -} - -.glyphicon-collapse-up:before { - content: "\e160" -} - -.glyphicon-log-in:before { - content: "\e161" -} - -.glyphicon-flash:before { - content: "\e162" -} - -.glyphicon-log-out:before { - content: "\e163" -} - -.glyphicon-new-window:before { - content: "\e164" -} - -.glyphicon-record:before { - content: "\e165" -} - -.glyphicon-save:before { - content: "\e166" -} - -.glyphicon-open:before { - content: "\e167" -} - -.glyphicon-saved:before { - content: "\e168" -} - -.glyphicon-import:before { - content: "\e169" -} - -.glyphicon-export:before { - content: "\e170" -} - -.glyphicon-send:before { - content: "\e171" -} - -.glyphicon-floppy-disk:before { - content: "\e172" -} - -.glyphicon-floppy-saved:before { - content: "\e173" -} - -.glyphicon-floppy-remove:before { - content: "\e174" -} - -.glyphicon-floppy-save:before { - content: "\e175" -} - -.glyphicon-floppy-open:before { - content: "\e176" -} - -.glyphicon-credit-card:before { - content: "\e177" -} - -.glyphicon-transfer:before { - content: "\e178" -} - -.glyphicon-cutlery:before { - content: "\e179" -} - -.glyphicon-header:before { - content: "\e180" -} - -.glyphicon-compressed:before { - content: "\e181" -} - -.glyphicon-earphone:before { - content: "\e182" -} - -.glyphicon-phone-alt:before { - content: "\e183" -} - -.glyphicon-tower:before { - content: "\e184" -} - -.glyphicon-stats:before { - content: "\e185" -} - -.glyphicon-sd-video:before { - content: "\e186" -} - -.glyphicon-hd-video:before { - content: "\e187" -} - -.glyphicon-subtitles:before { - content: "\e188" -} - -.glyphicon-sound-stereo:before { - content: "\e189" -} - -.glyphicon-sound-dolby:before { - content: "\e190" -} - -.glyphicon-sound-5-1:before { - content: "\e191" -} - -.glyphicon-sound-6-1:before { - content: "\e192" -} - -.glyphicon-sound-7-1:before { - content: "\e193" -} - -.glyphicon-copyright-mark:before { - content: "\e194" -} - -.glyphicon-registration-mark:before { - content: "\e195" -} - -.glyphicon-cloud-download:before { - content: "\e197" -} - -.glyphicon-cloud-upload:before { - content: "\e198" -} - -.glyphicon-tree-conifer:before { - content: "\e199" -} - -.glyphicon-tree-deciduous:before { - content: "\e200" -} - -.glyphicon-cd:before { - content: "\e201" -} - -.glyphicon-save-file:before { - content: "\e202" -} - -.glyphicon-open-file:before { - content: "\e203" -} - -.glyphicon-level-up:before { - content: "\e204" -} - -.glyphicon-copy:before { - content: "\e205" -} - -.glyphicon-paste:before { - content: "\e206" -} - -.glyphicon-alert:before { - content: "\e209" -} - -.glyphicon-equalizer:before { - content: "\e210" -} - -.glyphicon-king:before { - content: "\e211" -} - -.glyphicon-queen:before { - content: "\e212" -} - -.glyphicon-pawn:before { - content: "\e213" -} - -.glyphicon-bishop:before { - content: "\e214" -} - -.glyphicon-knight:before { - content: "\e215" -} - -.glyphicon-baby-formula:before { - content: "\e216" -} - -.glyphicon-tent:before { - content: "\26fa" -} - -.glyphicon-blackboard:before { - content: "\e218" -} - -.glyphicon-bed:before { - content: "\e219" -} - -.glyphicon-apple:before { - content: "\f8ff" -} - -.glyphicon-erase:before { - content: "\e221" -} - -.glyphicon-hourglass:before { - content: "\231b" -} - -.glyphicon-lamp:before { - content: "\e223" -} - -.glyphicon-duplicate:before { - content: "\e224" -} - -.glyphicon-piggy-bank:before { - content: "\e225" -} - -.glyphicon-scissors:before { - content: "\e226" -} - -.glyphicon-bitcoin:before { - content: "\e227" -} - -.glyphicon-btc:before { - content: "\e227" -} - -.glyphicon-xbt:before { - content: "\e227" -} - -.glyphicon-yen:before { - content: "\00a5" -} - -.glyphicon-jpy:before { - content: "\00a5" -} - -.glyphicon-ruble:before { - content: "\20bd" -} - -.glyphicon-rub:before { - content: "\20bd" -} - -.glyphicon-scale:before { - content: "\e230" -} - -.glyphicon-ice-lolly:before { - content: "\e231" -} - -.glyphicon-ice-lolly-tasted:before { - content: "\e232" -} - -.glyphicon-education:before { - content: "\e233" -} - -.glyphicon-option-horizontal:before { - content: "\e234" -} - -.glyphicon-option-vertical:before { - content: "\e235" -} - -.glyphicon-menu-hamburger:before { - content: "\e236" -} - -.glyphicon-modal-window:before { - content: "\e237" -} - -.glyphicon-oil:before { - content: "\e238" -} - -.glyphicon-grain:before { - content: "\e239" -} - -.glyphicon-sunglasses:before { - content: "\e240" -} - -.glyphicon-text-size:before { - content: "\e241" -} - -.glyphicon-text-color:before { - content: "\e242" -} - -.glyphicon-text-background:before { - content: "\e243" -} - -.glyphicon-object-align-top:before { - content: "\e244" -} - -.glyphicon-object-align-bottom:before { - content: "\e245" -} - -.glyphicon-object-align-horizontal:before { - content: "\e246" -} - -.glyphicon-object-align-left:before { - content: "\e247" -} - -.glyphicon-object-align-vertical:before { - content: "\e248" -} - -.glyphicon-object-align-right:before { - content: "\e249" -} - -.glyphicon-triangle-right:before { - content: "\e250" -} - -.glyphicon-triangle-left:before { - content: "\e251" -} - -.glyphicon-triangle-bottom:before { - content: "\e252" -} - -.glyphicon-triangle-top:before { - content: "\e253" -} - -.glyphicon-console:before { - content: "\e254" -} - -.glyphicon-superscript:before { - content: "\e255" -} - -.glyphicon-subscript:before { - content: "\e256" -} - -.glyphicon-menu-left:before { - content: "\e257" -} - -.glyphicon-menu-right:before { - content: "\e258" -} - -.glyphicon-menu-down:before { - content: "\e259" -} - -.glyphicon-menu-up:before { - content: "\e260" -} - -* { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box -} - -:after, :before { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box -} - -html { - font-size: 10px; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0) -} - -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.42857143; - color: #333; - background-color: #fff -} - -button, input, select, textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit -} - -a { - color: #337ab7; - text-decoration: none -} - -a:focus, a:hover { - color: #23527c; - text-decoration: underline -} - -a:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px -} - -figure { - margin: 0 -} - -img { - vertical-align: middle -} - -.carousel-inner>.item>a>img, .carousel-inner>.item>img, .img-responsive, - .thumbnail a>img, .thumbnail>img { - display: block; - max-width: 100%; - height: auto -} - -.img-rounded { - border-radius: 6px -} - -.img-thumbnail { - display: inline-block; - max-width: 100%; - height: auto; - padding: 4px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: all .2s ease-in-out; - -o-transition: all .2s ease-in-out; - transition: all .2s ease-in-out -} - -.img-circle { - border-radius: 50% -} - -hr { - margin-top: 20px; - margin-bottom: 20px; - border: 0; - border-top: 1px solid #eee -} - -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0 -} - -.sr-only-focusable:active, .sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto -} - -[role=button] { - cursor: pointer -} - -.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit -} - -.h1 .small, .h1 small, .h2 .small, .h2 small, .h3 .small, .h3 small, .h4 .small, - .h4 small, .h5 .small, .h5 small, .h6 .small, .h6 small, h1 .small, h1 small, - h2 .small, h2 small, h3 .small, h3 small, h4 .small, h4 small, h5 .small, - h5 small, h6 .small, h6 small { - font-weight: 400; - line-height: 1; - color: #777 -} - -.h1, .h2, .h3, h1, h2, h3 { - margin-top: 20px; - margin-bottom: 10px -} - -.h1 .small, .h1 small, .h2 .small, .h2 small, .h3 .small, .h3 small, h1 .small, - h1 small, h2 .small, h2 small, h3 .small, h3 small { - font-size: 65% -} - -.h4, .h5, .h6, h4, h5, h6 { - margin-top: 10px; - margin-bottom: 10px -} - -.h4 .small, .h4 small, .h5 .small, .h5 small, .h6 .small, .h6 small, h4 .small, - h4 small, h5 .small, h5 small, h6 .small, h6 small { - font-size: 75% -} - -.h1, h1 { - font-size: 36px -} - -.h2, h2 { - font-size: 30px -} - -.h3, h3 { - font-size: 24px -} - -.h4, h4 { - font-size: 18px -} - -.h5, h5 { - font-size: 14px -} - -.h6, h6 { - font-size: 12px -} - -p { - margin: 0 0 10px -} - -.lead { - margin-bottom: 20px; - font-size: 16px; - font-weight: 300; - line-height: 1.4 -} - -@media ( min-width :768px) { - .lead { - font-size: 21px - } -} - -.small, small { - font-size: 85% -} - -.mark, mark { - padding: .2em; - background-color: #fcf8e3 -} - -.text-left { - text-align: left -} - -.text-right { - text-align: right -} - -.text-center { - text-align: center -} - -.text-justify { - text-align: justify -} - -.text-nowrap { - white-space: nowrap -} - -.text-lowercase { - text-transform: lowercase -} - -.text-uppercase { - text-transform: uppercase -} - -.text-capitalize { - text-transform: capitalize -} - -.text-muted { - color: #777 -} - -.text-primary { - color: #337ab7 -} - -a.text-primary:hover { - color: #286090 -} - -.text-success { - color: #3c763d -} - -a.text-success:hover { - color: #2b542c -} - -.text-info { - color: #31708f -} - -a.text-info:hover { - color: #245269 -} - -.text-warning { - color: #8a6d3b -} - -a.text-warning:hover { - color: #66512c -} - -.text-danger { - color: #a94442 -} - -a.text-danger:hover { - color: #843534 -} - -.bg-primary { - color: #fff; - background-color: #337ab7 -} - -a.bg-primary:hover { - background-color: #286090 -} - -.bg-success { - background-color: #dff0d8 -} - -a.bg-success:hover { - background-color: #c1e2b3 -} - -.bg-info { - background-color: #d9edf7 -} - -a.bg-info:hover { - background-color: #afd9ee -} - -.bg-warning { - background-color: #fcf8e3 -} - -a.bg-warning:hover { - background-color: #f7ecb5 -} - -.bg-danger { - background-color: #f2dede -} - -a.bg-danger:hover { - background-color: #e4b9b9 -} - -.page-header { - padding-bottom: 9px; - margin: 40px 0 20px; - border-bottom: 1px solid #eee -} - -ol, ul { - margin-top: 0; - margin-bottom: 10px -} - -ol ol, ol ul, ul ol, ul ul { - margin-bottom: 0 -} - -.list-unstyled { - padding-left: 0; - list-style: none -} - -.list-inline { - padding-left: 0; - margin-left: -5px; - list-style: none -} - -.list-inline>li { - display: inline-block; - padding-right: 5px; - padding-left: 5px -} - -dl { - margin-top: 0; - margin-bottom: 20px -} - -dd, dt { - line-height: 1.42857143 -} - -dt { - font-weight: 700 -} - -dd { - margin-left: 0 -} - -@media ( min-width :768px) { - .dl-horizontal dt { - float: left; - width: 160px; - overflow: hidden; - clear: left; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap - } - .dl-horizontal dd { - margin-left: 180px - } -} - -abbr[data-original-title], abbr[title] { - cursor: help; - border-bottom: 1px dotted #777 -} - -.initialism { - font-size: 90%; - text-transform: uppercase -} - -blockquote { - padding: 10px 20px; - margin: 0 0 20px; - font-size: 17.5px; - border-left: 5px solid #eee -} - -blockquote ol:last-child, blockquote p:last-child, blockquote ul:last-child - { - margin-bottom: 0 -} - -blockquote .small, blockquote footer, blockquote small { - display: block; - font-size: 80%; - line-height: 1.42857143; - color: #777 -} - -blockquote .small:before, blockquote footer:before, blockquote small:before - { - content: '\2014 \00A0' -} - -.blockquote-reverse, blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - text-align: right; - border-right: 5px solid #eee; - border-left: 0 -} - -.blockquote-reverse .small:before, .blockquote-reverse footer:before, - .blockquote-reverse small:before, blockquote.pull-right .small:before, - blockquote.pull-right footer:before, blockquote.pull-right small:before - { - content: '' -} - -.blockquote-reverse .small:after, .blockquote-reverse footer:after, - .blockquote-reverse small:after, blockquote.pull-right .small:after, - blockquote.pull-right footer:after, blockquote.pull-right small:after { - content: '\00A0 \2014' -} - -address { - margin-bottom: 20px; - font-style: normal; - line-height: 1.42857143 -} - -code, kbd, pre, samp { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace -} - -code { - padding: 2px 4px; - font-size: 90%; - color: #c7254e; - background-color: #f9f2f4; - border-radius: 4px -} - -kbd { - padding: 2px 4px; - font-size: 90%; - color: #fff; - background-color: #333; - border-radius: 3px; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25) -} - -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: 700; - -webkit-box-shadow: none; - box-shadow: none -} - -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 1.42857143; - color: #333; - word-break: break-all; - word-wrap: break-word; - background-color: #f5f5f5; - border: 1px solid #ccc; - border-radius: 4px -} - -pre code { - padding: 0; - font-size: inherit; - color: inherit; - white-space: pre-wrap; - background-color: transparent; - border-radius: 0 -} - -.pre-scrollable { - max-height: 340px; - overflow-y: scroll -} - -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto -} - -@media ( min-width :768px) { - .container { - width: 750px - } -} - -@media ( min-width :992px) { - .container { - width: 970px - } -} - -@media ( min-width :1200px) { - .container { - width: 1170px - } -} - -.container-fluid { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto -} - -.row { - margin-right: -15px; - margin-left: -15px -} - -.col-lg-1, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-2, .col-lg-3, - .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, - .col-md-1, .col-md-10, .col-md-11, .col-md-12, .col-md-2, .col-md-3, - .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, - .col-sm-1, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-2, .col-sm-3, - .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, - .col-xs-1, .col-xs-10, .col-xs-11, .col-xs-12, .col-xs-2, .col-xs-3, - .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9 { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px -} - -.col-xs-1, .col-xs-10, .col-xs-11, .col-xs-12, .col-xs-2, .col-xs-3, - .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9 { - float: left -} - -.col-xs-12 { - width: 100% -} - -.col-xs-11 { - width: 91.66666667% -} - -.col-xs-10 { - width: 83.33333333% -} - -.col-xs-9 { - width: 75% -} - -.col-xs-8 { - width: 66.66666667% -} - -.col-xs-7 { - width: 58.33333333% -} - -.col-xs-6 { - width: 50% -} - -.col-xs-5 { - width: 41.66666667% -} - -.col-xs-4 { - width: 33.33333333% -} - -.col-xs-3 { - width: 25% -} - -.col-xs-2 { - width: 16.66666667% -} - -.col-xs-1 { - width: 8.33333333% -} - -.col-xs-pull-12 { - right: 100% -} - -.col-xs-pull-11 { - right: 91.66666667% -} - -.col-xs-pull-10 { - right: 83.33333333% -} - -.col-xs-pull-9 { - right: 75% -} - -.col-xs-pull-8 { - right: 66.66666667% -} - -.col-xs-pull-7 { - right: 58.33333333% -} - -.col-xs-pull-6 { - right: 50% -} - -.col-xs-pull-5 { - right: 41.66666667% -} - -.col-xs-pull-4 { - right: 33.33333333% -} - -.col-xs-pull-3 { - right: 25% -} - -.col-xs-pull-2 { - right: 16.66666667% -} - -.col-xs-pull-1 { - right: 8.33333333% -} - -.col-xs-pull-0 { - right: auto -} - -.col-xs-push-12 { - left: 100% -} - -.col-xs-push-11 { - left: 91.66666667% -} - -.col-xs-push-10 { - left: 83.33333333% -} - -.col-xs-push-9 { - left: 75% -} - -.col-xs-push-8 { - left: 66.66666667% -} - -.col-xs-push-7 { - left: 58.33333333% -} - -.col-xs-push-6 { - left: 50% -} - -.col-xs-push-5 { - left: 41.66666667% -} - -.col-xs-push-4 { - left: 33.33333333% -} - -.col-xs-push-3 { - left: 25% -} - -.col-xs-push-2 { - left: 16.66666667% -} - -.col-xs-push-1 { - left: 8.33333333% -} - -.col-xs-push-0 { - left: auto -} - -.col-xs-offset-12 { - margin-left: 100% -} - -.col-xs-offset-11 { - margin-left: 91.66666667% -} - -.col-xs-offset-10 { - margin-left: 83.33333333% -} - -.col-xs-offset-9 { - margin-left: 75% -} - -.col-xs-offset-8 { - margin-left: 66.66666667% -} - -.col-xs-offset-7 { - margin-left: 58.33333333% -} - -.col-xs-offset-6 { - margin-left: 50% -} - -.col-xs-offset-5 { - margin-left: 41.66666667% -} - -.col-xs-offset-4 { - margin-left: 33.33333333% -} - -.col-xs-offset-3 { - margin-left: 25% -} - -.col-xs-offset-2 { - margin-left: 16.66666667% -} - -.col-xs-offset-1 { - margin-left: 8.33333333% -} - -.col-xs-offset-0 { - margin-left: 0 -} - -@media ( min-width :768px) { - .col-sm-1, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-2, .col-sm-3, - .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9 { - float: left - } - .col-sm-12 { - width: 100% - } - .col-sm-11 { - width: 91.66666667% - } - .col-sm-10 { - width: 83.33333333% - } - .col-sm-9 { - width: 75% - } - .col-sm-8 { - width: 66.66666667% - } - .col-sm-7 { - width: 58.33333333% - } - .col-sm-6 { - width: 50% - } - .col-sm-5 { - width: 41.66666667% - } - .col-sm-4 { - width: 33.33333333% - } - .col-sm-3 { - width: 25% - } - .col-sm-2 { - width: 16.66666667% - } - .col-sm-1 { - width: 8.33333333% - } - .col-sm-pull-12 { - right: 100% - } - .col-sm-pull-11 { - right: 91.66666667% - } - .col-sm-pull-10 { - right: 83.33333333% - } - .col-sm-pull-9 { - right: 75% - } - .col-sm-pull-8 { - right: 66.66666667% - } - .col-sm-pull-7 { - right: 58.33333333% - } - .col-sm-pull-6 { - right: 50% - } - .col-sm-pull-5 { - right: 41.66666667% - } - .col-sm-pull-4 { - right: 33.33333333% - } - .col-sm-pull-3 { - right: 25% - } - .col-sm-pull-2 { - right: 16.66666667% - } - .col-sm-pull-1 { - right: 8.33333333% - } - .col-sm-pull-0 { - right: auto - } - .col-sm-push-12 { - left: 100% - } - .col-sm-push-11 { - left: 91.66666667% - } - .col-sm-push-10 { - left: 83.33333333% - } - .col-sm-push-9 { - left: 75% - } - .col-sm-push-8 { - left: 66.66666667% - } - .col-sm-push-7 { - left: 58.33333333% - } - .col-sm-push-6 { - left: 50% - } - .col-sm-push-5 { - left: 41.66666667% - } - .col-sm-push-4 { - left: 33.33333333% - } - .col-sm-push-3 { - left: 25% - } - .col-sm-push-2 { - left: 16.66666667% - } - .col-sm-push-1 { - left: 8.33333333% - } - .col-sm-push-0 { - left: auto - } - .col-sm-offset-12 { - margin-left: 100% - } - .col-sm-offset-11 { - margin-left: 91.66666667% - } - .col-sm-offset-10 { - margin-left: 83.33333333% - } - .col-sm-offset-9 { - margin-left: 75% - } - .col-sm-offset-8 { - margin-left: 66.66666667% - } - .col-sm-offset-7 { - margin-left: 58.33333333% - } - .col-sm-offset-6 { - margin-left: 50% - } - .col-sm-offset-5 { - margin-left: 41.66666667% - } - .col-sm-offset-4 { - margin-left: 33.33333333% - } - .col-sm-offset-3 { - margin-left: 25% - } - .col-sm-offset-2 { - margin-left: 16.66666667% - } - .col-sm-offset-1 { - margin-left: 8.33333333% - } - .col-sm-offset-0 { - margin-left: 0 - } -} - -@media ( min-width :992px) { - .col-md-1, .col-md-10, .col-md-11, .col-md-12, .col-md-2, .col-md-3, - .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9 { - float: left - } - .col-md-12 { - width: 100% - } - .col-md-11 { - width: 91.66666667% - } - .col-md-10 { - width: 83.33333333% - } - .col-md-9 { - width: 75% - } - .col-md-8 { - width: 66.66666667% - } - .col-md-7 { - width: 58.33333333% - } - .col-md-6 { - width: 50% - } - .col-md-5 { - width: 41.66666667% - } - .col-md-4 { - width: 33.33333333% - } - .col-md-3 { - width: 25% - } - .col-md-2 { - width: 16.66666667% - } - .col-md-1 { - width: 8.33333333% - } - .col-md-pull-12 { - right: 100% - } - .col-md-pull-11 { - right: 91.66666667% - } - .col-md-pull-10 { - right: 83.33333333% - } - .col-md-pull-9 { - right: 75% - } - .col-md-pull-8 { - right: 66.66666667% - } - .col-md-pull-7 { - right: 58.33333333% - } - .col-md-pull-6 { - right: 50% - } - .col-md-pull-5 { - right: 41.66666667% - } - .col-md-pull-4 { - right: 33.33333333% - } - .col-md-pull-3 { - right: 25% - } - .col-md-pull-2 { - right: 16.66666667% - } - .col-md-pull-1 { - right: 8.33333333% - } - .col-md-pull-0 { - right: auto - } - .col-md-push-12 { - left: 100% - } - .col-md-push-11 { - left: 91.66666667% - } - .col-md-push-10 { - left: 83.33333333% - } - .col-md-push-9 { - left: 75% - } - .col-md-push-8 { - left: 66.66666667% - } - .col-md-push-7 { - left: 58.33333333% - } - .col-md-push-6 { - left: 50% - } - .col-md-push-5 { - left: 41.66666667% - } - .col-md-push-4 { - left: 33.33333333% - } - .col-md-push-3 { - left: 25% - } - .col-md-push-2 { - left: 16.66666667% - } - .col-md-push-1 { - left: 8.33333333% - } - .col-md-push-0 { - left: auto - } - .col-md-offset-12 { - margin-left: 100% - } - .col-md-offset-11 { - margin-left: 91.66666667% - } - .col-md-offset-10 { - margin-left: 83.33333333% - } - .col-md-offset-9 { - margin-left: 75% - } - .col-md-offset-8 { - margin-left: 66.66666667% - } - .col-md-offset-7 { - margin-left: 58.33333333% - } - .col-md-offset-6 { - margin-left: 50% - } - .col-md-offset-5 { - margin-left: 41.66666667% - } - .col-md-offset-4 { - margin-left: 33.33333333% - } - .col-md-offset-3 { - margin-left: 25% - } - .col-md-offset-2 { - margin-left: 16.66666667% - } - .col-md-offset-1 { - margin-left: 8.33333333% - } - .col-md-offset-0 { - margin-left: 0 - } -} - -@media ( min-width :1200px) { - .col-lg-1, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-2, .col-lg-3, - .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9 { - float: left - } - .col-lg-12 { - width: 100% - } - .col-lg-11 { - width: 91.66666667% - } - .col-lg-10 { - width: 83.33333333% - } - .col-lg-9 { - width: 75% - } - .col-lg-8 { - width: 66.66666667% - } - .col-lg-7 { - width: 58.33333333% - } - .col-lg-6 { - width: 50% - } - .col-lg-5 { - width: 41.66666667% - } - .col-lg-4 { - width: 33.33333333% - } - .col-lg-3 { - width: 25% - } - .col-lg-2 { - width: 16.66666667% - } - .col-lg-1 { - width: 8.33333333% - } - .col-lg-pull-12 { - right: 100% - } - .col-lg-pull-11 { - right: 91.66666667% - } - .col-lg-pull-10 { - right: 83.33333333% - } - .col-lg-pull-9 { - right: 75% - } - .col-lg-pull-8 { - right: 66.66666667% - } - .col-lg-pull-7 { - right: 58.33333333% - } - .col-lg-pull-6 { - right: 50% - } - .col-lg-pull-5 { - right: 41.66666667% - } - .col-lg-pull-4 { - right: 33.33333333% - } - .col-lg-pull-3 { - right: 25% - } - .col-lg-pull-2 { - right: 16.66666667% - } - .col-lg-pull-1 { - right: 8.33333333% - } - .col-lg-pull-0 { - right: auto - } - .col-lg-push-12 { - left: 100% - } - .col-lg-push-11 { - left: 91.66666667% - } - .col-lg-push-10 { - left: 83.33333333% - } - .col-lg-push-9 { - left: 75% - } - .col-lg-push-8 { - left: 66.66666667% - } - .col-lg-push-7 { - left: 58.33333333% - } - .col-lg-push-6 { - left: 50% - } - .col-lg-push-5 { - left: 41.66666667% - } - .col-lg-push-4 { - left: 33.33333333% - } - .col-lg-push-3 { - left: 25% - } - .col-lg-push-2 { - left: 16.66666667% - } - .col-lg-push-1 { - left: 8.33333333% - } - .col-lg-push-0 { - left: auto - } - .col-lg-offset-12 { - margin-left: 100% - } - .col-lg-offset-11 { - margin-left: 91.66666667% - } - .col-lg-offset-10 { - margin-left: 83.33333333% - } - .col-lg-offset-9 { - margin-left: 75% - } - .col-lg-offset-8 { - margin-left: 66.66666667% - } - .col-lg-offset-7 { - margin-left: 58.33333333% - } - .col-lg-offset-6 { - margin-left: 50% - } - .col-lg-offset-5 { - margin-left: 41.66666667% - } - .col-lg-offset-4 { - margin-left: 33.33333333% - } - .col-lg-offset-3 { - margin-left: 25% - } - .col-lg-offset-2 { - margin-left: 16.66666667% - } - .col-lg-offset-1 { - margin-left: 8.33333333% - } - .col-lg-offset-0 { - margin-left: 0 - } -} - -table { - background-color: transparent -} - -caption { - padding-top: 8px; - padding-bottom: 8px; - color: #777; - text-align: left -} - -th { - text-align: left -} - -.table { - width: 100%; - max-width: 100%; - margin-bottom: 20px -} - -.table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, - .table>thead>tr>td, .table>thead>tr>th { - padding: 8px; - line-height: 1.42857143; - vertical-align: top; - border-top: 1px solid #ddd -} - -.table>thead>tr>th { - vertical-align: bottom; - border-bottom: 2px solid #ddd -} - -.table>caption+thead>tr:first-child>td, .table>caption+thead>tr:first-child>th, - .table>colgroup+thead>tr:first-child>td, .table>colgroup+thead>tr:first-child>th, - .table>thead:first-child>tr:first-child>td, .table>thead:first-child>tr:first-child>th - { - border-top: 0 -} - -.table>tbody+tbody { - border-top: 2px solid #ddd -} - -.table .table { - background-color: #fff -} - -.table-condensed>tbody>tr>td, .table-condensed>tbody>tr>th, - .table-condensed>tfoot>tr>td, .table-condensed>tfoot>tr>th, - .table-condensed>thead>tr>td, .table-condensed>thead>tr>th { - padding: 5px -} - -.table-bordered { - border: 1px solid #ddd -} - -.table-bordered>tbody>tr>td, .table-bordered>tbody>tr>th, - .table-bordered>tfoot>tr>td, .table-bordered>tfoot>tr>th, - .table-bordered>thead>tr>td, .table-bordered>thead>tr>th { - border: 1px solid #ddd -} - -.table-bordered>thead>tr>td, .table-bordered>thead>tr>th { - border-bottom-width: 2px -} - -.table-striped>tbody>tr:nth-of-type(odd) { - background-color: #f9f9f9 -} - -.table-hover>tbody>tr:hover { - background-color: #f5f5f5 -} - -table col[class*=col-] { - position: static; - display: table-column; - float: none -} - -table td[class*=col-], table th[class*=col-] { - position: static; - display: table-cell; - float: none -} - -.table>tbody>tr.active>td, .table>tbody>tr.active>th, .table>tbody>tr>td.active, - .table>tbody>tr>th.active, .table>tfoot>tr.active>td, .table>tfoot>tr.active>th, - .table>tfoot>tr>td.active, .table>tfoot>tr>th.active, .table>thead>tr.active>td, - .table>thead>tr.active>th, .table>thead>tr>td.active, .table>thead>tr>th.active - { - background-color: #f5f5f5 -} - -.table-hover>tbody>tr.active:hover>td, .table-hover>tbody>tr.active:hover>th, - .table-hover>tbody>tr:hover>.active, .table-hover>tbody>tr>td.active:hover, - .table-hover>tbody>tr>th.active:hover { - background-color: #e8e8e8 -} - -.table>tbody>tr.success>td, .table>tbody>tr.success>th, .table>tbody>tr>td.success, - .table>tbody>tr>th.success, .table>tfoot>tr.success>td, .table>tfoot>tr.success>th, - .table>tfoot>tr>td.success, .table>tfoot>tr>th.success, .table>thead>tr.success>td, - .table>thead>tr.success>th, .table>thead>tr>td.success, .table>thead>tr>th.success - { - background-color: #dff0d8 -} - -.table-hover>tbody>tr.success:hover>td, .table-hover>tbody>tr.success:hover>th, - .table-hover>tbody>tr:hover>.success, .table-hover>tbody>tr>td.success:hover, - .table-hover>tbody>tr>th.success:hover { - background-color: #d0e9c6 -} - -.table>tbody>tr.info>td, .table>tbody>tr.info>th, .table>tbody>tr>td.info, - .table>tbody>tr>th.info, .table>tfoot>tr.info>td, .table>tfoot>tr.info>th, - .table>tfoot>tr>td.info, .table>tfoot>tr>th.info, .table>thead>tr.info>td, - .table>thead>tr.info>th, .table>thead>tr>td.info, .table>thead>tr>th.info - { - background-color: #d9edf7 -} - -.table-hover>tbody>tr.info:hover>td, .table-hover>tbody>tr.info:hover>th, - .table-hover>tbody>tr:hover>.info, .table-hover>tbody>tr>td.info:hover, - .table-hover>tbody>tr>th.info:hover { - background-color: #c4e3f3 -} - -.table>tbody>tr.warning>td, .table>tbody>tr.warning>th, .table>tbody>tr>td.warning, - .table>tbody>tr>th.warning, .table>tfoot>tr.warning>td, .table>tfoot>tr.warning>th, - .table>tfoot>tr>td.warning, .table>tfoot>tr>th.warning, .table>thead>tr.warning>td, - .table>thead>tr.warning>th, .table>thead>tr>td.warning, .table>thead>tr>th.warning - { - background-color: #fcf8e3 -} - -.table-hover>tbody>tr.warning:hover>td, .table-hover>tbody>tr.warning:hover>th, - .table-hover>tbody>tr:hover>.warning, .table-hover>tbody>tr>td.warning:hover, - .table-hover>tbody>tr>th.warning:hover { - background-color: #faf2cc -} - -.table>tbody>tr.danger>td, .table>tbody>tr.danger>th, .table>tbody>tr>td.danger, - .table>tbody>tr>th.danger, .table>tfoot>tr.danger>td, .table>tfoot>tr.danger>th, - .table>tfoot>tr>td.danger, .table>tfoot>tr>th.danger, .table>thead>tr.danger>td, - .table>thead>tr.danger>th, .table>thead>tr>td.danger, .table>thead>tr>th.danger - { - background-color: #f2dede -} - -.table-hover>tbody>tr.danger:hover>td, .table-hover>tbody>tr.danger:hover>th, - .table-hover>tbody>tr:hover>.danger, .table-hover>tbody>tr>td.danger:hover, - .table-hover>tbody>tr>th.danger:hover { - background-color: #ebcccc -} - -.table-responsive { - min-height: .01%; - overflow-x: auto -} - -@media screen and (max-width:767px) { - .table-responsive { - width: 100%; - margin-bottom: 15px; - overflow-y: hidden; - -ms-overflow-style: -ms-autohiding-scrollbar; - border: 1px solid #ddd - } - .table-responsive>.table { - margin-bottom: 0 - } - .table-responsive>.table>tbody>tr>td, .table-responsive>.table>tbody>tr>th, - .table-responsive>.table>tfoot>tr>td, .table-responsive>.table>tfoot>tr>th, - .table-responsive>.table>thead>tr>td, .table-responsive>.table>thead>tr>th - { - white-space: nowrap - } - .table-responsive>.table-bordered { - border: 0 - } - .table-responsive>.table-bordered>tbody>tr>td:first-child, - .table-responsive>.table-bordered>tbody>tr>th:first-child, - .table-responsive>.table-bordered>tfoot>tr>td:first-child, - .table-responsive>.table-bordered>tfoot>tr>th:first-child, - .table-responsive>.table-bordered>thead>tr>td:first-child, - .table-responsive>.table-bordered>thead>tr>th:first-child { - border-left: 0 - } - .table-responsive>.table-bordered>tbody>tr>td:last-child, - .table-responsive>.table-bordered>tbody>tr>th:last-child, - .table-responsive>.table-bordered>tfoot>tr>td:last-child, - .table-responsive>.table-bordered>tfoot>tr>th:last-child, - .table-responsive>.table-bordered>thead>tr>td:last-child, - .table-responsive>.table-bordered>thead>tr>th:last-child { - border-right: 0 - } - .table-responsive>.table-bordered>tbody>tr:last-child>td, - .table-responsive>.table-bordered>tbody>tr:last-child>th, - .table-responsive>.table-bordered>tfoot>tr:last-child>td, - .table-responsive>.table-bordered>tfoot>tr:last-child>th { - border-bottom: 0 - } -} - -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0 -} - -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: inherit; - color: #333; - border: 0; - border-bottom: 1px solid #e5e5e5 -} - -label { - display: inline-block; - max-width: 100%; - margin-bottom: 5px; - font-weight: 700 -} - -input[type=search] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box -} - -input[type=checkbox], input[type=radio] { - margin: 4px 0 0; - margin-top: 1px \9; - line-height: normal -} - -input[type=file] { - display: block -} - -input[type=range] { - display: block; - width: 100% -} - -select[multiple], select[size] { - height: auto -} - -input[type=file]:focus, input[type=checkbox]:focus, input[type=radio]:focus - { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px -} - -output { - display: block; - padding-top: 7px; - font-size: 14px; - line-height: 1.42857143; - color: #555 -} - -.form-control { - display: block; - width: 100%; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.42857143; - color: #555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow - ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out - .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s -} - -.form-control:focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px - rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px - rgba(102, 175, 233, .6) -} - -.form-control::-moz-placeholder { - color: #999; - opacity: 1 -} - -.form-control:-ms-input-placeholder { - color: #999 -} - -.form-control::-webkit-input-placeholder { - color: #999 -} - -.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control - { - background-color: #eee; - opacity: 1 -} - -.form-control[disabled], fieldset[disabled] .form-control { - cursor: not-allowed -} - -textarea.form-control { - height: auto -} - -input[type=search] { - -webkit-appearance: none -} - -@media screen and (-webkit-min-device-pixel-ratio:0) { - input[type=date], input[type=time], input[type=datetime-local], input[type=month] - { - line-height: 34px - } - .input-group-sm input[type=date], .input-group-sm input[type=time], - .input-group-sm input[type=datetime-local], .input-group-sm input[type=month], - input[type=date].input-sm, input[type=time].input-sm, input[type=datetime-local].input-sm, - input[type=month].input-sm { - line-height: 30px - } - .input-group-lg input[type=date], .input-group-lg input[type=time], - .input-group-lg input[type=datetime-local], .input-group-lg input[type=month], - input[type=date].input-lg, input[type=time].input-lg, input[type=datetime-local].input-lg, - input[type=month].input-lg { - line-height: 46px - } -} - -.form-group { - margin-bottom: 15px -} - -.checkbox, .radio { - position: relative; - display: block; - margin-top: 10px; - margin-bottom: 10px -} - -.checkbox label, .radio label { - min-height: 20px; - padding-left: 20px; - margin-bottom: 0; - font-weight: 400; - cursor: pointer -} - -.checkbox input[type=checkbox], .checkbox-inline input[type=checkbox], - .radio input[type=radio], .radio-inline input[type=radio] { - position: absolute; - margin-top: 4px \9; - margin-left: -20px -} - -.checkbox+.checkbox, .radio+.radio { - margin-top: -5px -} - -.checkbox-inline, .radio-inline { - position: relative; - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - font-weight: 400; - vertical-align: middle; - cursor: pointer -} - -.checkbox-inline+.checkbox-inline, .radio-inline+.radio-inline { - margin-top: 0; - margin-left: 10px -} - -fieldset[disabled] input[type=checkbox], fieldset[disabled] input[type=radio], - input[type=checkbox].disabled, input[type=checkbox][disabled], input[type=radio].disabled, - input[type=radio][disabled] { - cursor: not-allowed -} - -.checkbox-inline.disabled, .radio-inline.disabled, fieldset[disabled] .checkbox-inline, - fieldset[disabled] .radio-inline { - cursor: not-allowed -} - -.checkbox.disabled label, .radio.disabled label, fieldset[disabled] .checkbox label, - fieldset[disabled] .radio label { - cursor: not-allowed -} - -.form-control-static { - min-height: 34px; - padding-top: 7px; - padding-bottom: 7px; - margin-bottom: 0 -} - -.form-control-static.input-lg, .form-control-static.input-sm { - padding-right: 0; - padding-left: 0 -} - -.input-sm { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px -} - -select.input-sm { - height: 30px; - line-height: 30px -} - -select[multiple].input-sm, textarea.input-sm { - height: auto -} - -.form-group-sm .form-control { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px -} - -select.form-group-sm .form-control { - height: 30px; - line-height: 30px -} - -select[multiple].form-group-sm .form-control, textarea.form-group-sm .form-control - { - height: auto -} - -.form-group-sm .form-control-static { - height: 30px; - min-height: 32px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5 -} - -.input-lg { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px -} - -select.input-lg { - height: 46px; - line-height: 46px -} - -select[multiple].input-lg, textarea.input-lg { - height: auto -} - -.form-group-lg .form-control { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px -} - -select.form-group-lg .form-control { - height: 46px; - line-height: 46px -} - -select[multiple].form-group-lg .form-control, textarea.form-group-lg .form-control - { - height: auto -} - -.form-group-lg .form-control-static { - height: 46px; - min-height: 38px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333 -} - -.has-feedback { - position: relative -} - -.has-feedback .form-control { - padding-right: 42.5px -} - -.form-control-feedback { - position: absolute; - top: 0; - right: 0; - z-index: 2; - display: block; - width: 34px; - height: 34px; - line-height: 34px; - text-align: center; - pointer-events: none -} - -.input-lg+.form-control-feedback { - width: 46px; - height: 46px; - line-height: 46px -} - -.input-sm+.form-control-feedback { - width: 30px; - height: 30px; - line-height: 30px -} - -.has-success .checkbox, .has-success .checkbox-inline, .has-success .control-label, - .has-success .help-block, .has-success .radio, .has-success .radio-inline, - .has-success.checkbox label, .has-success.checkbox-inline label, - .has-success.radio label, .has-success.radio-inline label { - color: #3c763d -} - -.has-success .form-control { - border-color: #3c763d; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075) -} - -.has-success .form-control:focus { - border-color: #2b542c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168 -} - -.has-success .input-group-addon { - color: #3c763d; - background-color: #dff0d8; - border-color: #3c763d -} - -.has-success .form-control-feedback { - color: #3c763d -} - -.has-warning .checkbox, .has-warning .checkbox-inline, .has-warning .control-label, - .has-warning .help-block, .has-warning .radio, .has-warning .radio-inline, - .has-warning.checkbox label, .has-warning.checkbox-inline label, - .has-warning.radio label, .has-warning.radio-inline label { - color: #8a6d3b -} - -.has-warning .form-control { - border-color: #8a6d3b; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075) -} - -.has-warning .form-control:focus { - border-color: #66512c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b -} - -.has-warning .input-group-addon { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #8a6d3b -} - -.has-warning .form-control-feedback { - color: #8a6d3b -} - -.has-error .checkbox, .has-error .checkbox-inline, .has-error .control-label, - .has-error .help-block, .has-error .radio, .has-error .radio-inline, - .has-error.checkbox label, .has-error.checkbox-inline label, .has-error.radio label, - .has-error.radio-inline label { - color: #a94442 -} - -.has-error .form-control { - border-color: #a94442; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075) -} - -.has-error .form-control:focus { - border-color: #843534; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483 -} - -.has-error .input-group-addon { - color: #a94442; - background-color: #f2dede; - border-color: #a94442 -} - -.has-error .form-control-feedback { - color: #a94442 -} - -.has-feedback label ~.form-control-feedback { - top: 25px -} - -.has-feedback label.sr-only ~.form-control-feedback { - top: 0 -} - -.help-block { - display: block; - margin-top: 5px; - margin-bottom: 10px; - color: #737373 -} - -@media ( min-width :768px) { - .form-inline .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle - } - .form-inline .form-control-static { - display: inline-block - } - .form-inline .input-group { - display: inline-table; - vertical-align: middle - } - .form-inline .input-group .form-control, .form-inline .input-group .input-group-addon, - .form-inline .input-group .input-group-btn { - width: auto - } - .form-inline .input-group>.form-control { - width: 100% - } - .form-inline .control-label { - margin-bottom: 0; - vertical-align: middle - } - .form-inline .checkbox, .form-inline .radio { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle - } - .form-inline .checkbox label, .form-inline .radio label { - padding-left: 0 - } - .form-inline .checkbox input[type=checkbox], .form-inline .radio input[type=radio] - { - position: relative; - margin-left: 0 - } - .form-inline .has-feedback .form-control-feedback { - top: 0 - } -} - -.form-horizontal .checkbox, .form-horizontal .checkbox-inline, - .form-horizontal .radio, .form-horizontal .radio-inline { - padding-top: 7px; - margin-top: 0; - margin-bottom: 0 -} - -.form-horizontal .checkbox, .form-horizontal .radio { - min-height: 27px -} - -.form-horizontal .form-group { - margin-right: -15px; - margin-left: -15px -} - -@media ( min-width :768px) { - .form-horizontal .control-label { - padding-top: 7px; - margin-bottom: 0; - text-align: right - } -} - -.form-horizontal .has-feedback .form-control-feedback { - right: 15px -} - -@media ( min-width :768px) { - .form-horizontal .form-group-lg .control-label { - padding-top: 14.33px - } -} - -@media ( min-width :768px) { - .form-horizontal .form-group-sm .control-label { - padding-top: 6px - } -} - -.btn { - display: inline-block; - padding: 6px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: 400; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -ms-touch-action: manipulation; - touch-action: manipulation; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-image: none; - border: 1px solid transparent; - border-radius: 4px -} - -.btn.active.focus, .btn.active:focus, .btn.focus, .btn:active.focus, - .btn:active:focus, .btn:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px -} - -.btn.focus, .btn:focus, .btn:hover { - color: #333; - text-decoration: none -} - -.btn.active, .btn:active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125) -} - -.btn.disabled, .btn[disabled], fieldset[disabled] .btn { - pointer-events: none; - cursor: not-allowed; - filter: alpha(opacity = 65); - -webkit-box-shadow: none; - box-shadow: none; - opacity: .65 -} - -.btn-default { - color: #333; - background-color: #fff; - border-color: #ccc -} - -.btn-default.active, .btn-default.focus, .btn-default:active, - .btn-default:focus, .btn-default:hover, .open>.dropdown-toggle.btn-default - { - color: #333; - background-color: #e6e6e6; - border-color: #adadad -} - -.btn-default.active, .btn-default:active, .open>.dropdown-toggle.btn-default - { - background-image: none -} - -.btn-default.disabled, .btn-default.disabled.active, .btn-default.disabled.focus, - .btn-default.disabled:active, .btn-default.disabled:focus, .btn-default.disabled:hover, - .btn-default[disabled], .btn-default[disabled].active, .btn-default[disabled].focus, - .btn-default[disabled]:active, .btn-default[disabled]:focus, - .btn-default[disabled]:hover, fieldset[disabled] .btn-default, fieldset[disabled] .btn-default.active, - fieldset[disabled] .btn-default.focus, fieldset[disabled] .btn-default:active, - fieldset[disabled] .btn-default:focus, fieldset[disabled] .btn-default:hover - { - background-color: #fff; - border-color: #ccc -} - -.btn-default .badge { - color: #fff; - background-color: #333 -} - -.btn-primary { - color: #fff; - background-color: #337ab7; - border-color: #2e6da4 -} - -.btn-primary.active, .btn-primary.focus, .btn-primary:active, - .btn-primary:focus, .btn-primary:hover, .open>.dropdown-toggle.btn-primary - { - color: #fff; - background-color: #286090; - border-color: #204d74 -} - -.btn-primary.active, .btn-primary:active, .open>.dropdown-toggle.btn-primary - { - background-image: none -} - -.btn-primary.disabled, .btn-primary.disabled.active, .btn-primary.disabled.focus, - .btn-primary.disabled:active, .btn-primary.disabled:focus, .btn-primary.disabled:hover, - .btn-primary[disabled], .btn-primary[disabled].active, .btn-primary[disabled].focus, - .btn-primary[disabled]:active, .btn-primary[disabled]:focus, - .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary, fieldset[disabled] .btn-primary.active, - fieldset[disabled] .btn-primary.focus, fieldset[disabled] .btn-primary:active, - fieldset[disabled] .btn-primary:focus, fieldset[disabled] .btn-primary:hover - { - background-color: #337ab7; - border-color: #2e6da4 -} - -.btn-primary .badge { - color: #337ab7; - background-color: #fff -} - -.btn-success { - color: #fff; - background-color: #5cb85c; - border-color: #4cae4c -} - -.btn-success.active, .btn-success.focus, .btn-success:active, - .btn-success:focus, .btn-success:hover, .open>.dropdown-toggle.btn-success - { - color: #fff; - background-color: #449d44; - border-color: #398439 -} - -.btn-success.active, .btn-success:active, .open>.dropdown-toggle.btn-success - { - background-image: none -} - -.btn-success.disabled, .btn-success.disabled.active, .btn-success.disabled.focus, - .btn-success.disabled:active, .btn-success.disabled:focus, .btn-success.disabled:hover, - .btn-success[disabled], .btn-success[disabled].active, .btn-success[disabled].focus, - .btn-success[disabled]:active, .btn-success[disabled]:focus, - .btn-success[disabled]:hover, fieldset[disabled] .btn-success, fieldset[disabled] .btn-success.active, - fieldset[disabled] .btn-success.focus, fieldset[disabled] .btn-success:active, - fieldset[disabled] .btn-success:focus, fieldset[disabled] .btn-success:hover - { - background-color: #5cb85c; - border-color: #4cae4c -} - -.btn-success .badge { - color: #5cb85c; - background-color: #fff -} - -.btn-info { - color: #fff; - background-color: #5bc0de; - border-color: #46b8da -} - -.btn-info.active, .btn-info.focus, .btn-info:active, .btn-info:focus, - .btn-info:hover, .open>.dropdown-toggle.btn-info { - color: #fff; - background-color: #31b0d5; - border-color: #269abc -} - -.btn-info.active, .btn-info:active, .open>.dropdown-toggle.btn-info { - background-image: none -} - -.btn-info.disabled, .btn-info.disabled.active, .btn-info.disabled.focus, - .btn-info.disabled:active, .btn-info.disabled:focus, .btn-info.disabled:hover, - .btn-info[disabled], .btn-info[disabled].active, .btn-info[disabled].focus, - .btn-info[disabled]:active, .btn-info[disabled]:focus, .btn-info[disabled]:hover, - fieldset[disabled] .btn-info, fieldset[disabled] .btn-info.active, - fieldset[disabled] .btn-info.focus, fieldset[disabled] .btn-info:active, - fieldset[disabled] .btn-info:focus, fieldset[disabled] .btn-info:hover - { - background-color: #5bc0de; - border-color: #46b8da -} - -.btn-info .badge { - color: #5bc0de; - background-color: #fff -} - -.btn-warning { - color: #fff; - background-color: #f0ad4e; - border-color: #eea236 -} - -.btn-warning.active, .btn-warning.focus, .btn-warning:active, - .btn-warning:focus, .btn-warning:hover, .open>.dropdown-toggle.btn-warning - { - color: #fff; - background-color: #ec971f; - border-color: #d58512 -} - -.btn-warning.active, .btn-warning:active, .open>.dropdown-toggle.btn-warning - { - background-image: none -} - -.btn-warning.disabled, .btn-warning.disabled.active, .btn-warning.disabled.focus, - .btn-warning.disabled:active, .btn-warning.disabled:focus, .btn-warning.disabled:hover, - .btn-warning[disabled], .btn-warning[disabled].active, .btn-warning[disabled].focus, - .btn-warning[disabled]:active, .btn-warning[disabled]:focus, - .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning, fieldset[disabled] .btn-warning.active, - fieldset[disabled] .btn-warning.focus, fieldset[disabled] .btn-warning:active, - fieldset[disabled] .btn-warning:focus, fieldset[disabled] .btn-warning:hover - { - background-color: #f0ad4e; - border-color: #eea236 -} - -.btn-warning .badge { - color: #f0ad4e; - background-color: #fff -} - -.btn-danger { - color: #fff; - background-color: #d9534f; - border-color: #d43f3a -} - -.btn-danger.active, .btn-danger.focus, .btn-danger:active, .btn-danger:focus, - .btn-danger:hover, .open>.dropdown-toggle.btn-danger { - color: #fff; - background-color: #c9302c; - border-color: #ac2925 -} - -.btn-danger.active, .btn-danger:active, .open>.dropdown-toggle.btn-danger - { - background-image: none -} - -.btn-danger.disabled, .btn-danger.disabled.active, .btn-danger.disabled.focus, - .btn-danger.disabled:active, .btn-danger.disabled:focus, .btn-danger.disabled:hover, - .btn-danger[disabled], .btn-danger[disabled].active, .btn-danger[disabled].focus, - .btn-danger[disabled]:active, .btn-danger[disabled]:focus, .btn-danger[disabled]:hover, - fieldset[disabled] .btn-danger, fieldset[disabled] .btn-danger.active, - fieldset[disabled] .btn-danger.focus, fieldset[disabled] .btn-danger:active, - fieldset[disabled] .btn-danger:focus, fieldset[disabled] .btn-danger:hover - { - background-color: #d9534f; - border-color: #d43f3a -} - -.btn-danger .badge { - color: #d9534f; - background-color: #fff -} - -.btn-link { - font-weight: 400; - color: #337ab7; - border-radius: 0 -} - -.btn-link, .btn-link.active, .btn-link:active, .btn-link[disabled], - fieldset[disabled] .btn-link { - background-color: transparent; - -webkit-box-shadow: none; - box-shadow: none -} - -.btn-link, .btn-link:active, .btn-link:focus, .btn-link:hover { - border-color: transparent -} - -.btn-link:focus, .btn-link:hover { - color: #23527c; - text-decoration: underline; - background-color: transparent -} - -.btn-link[disabled]:focus, .btn-link[disabled]:hover, fieldset[disabled] .btn-link:focus, - fieldset[disabled] .btn-link:hover { - color: #777; - text-decoration: none -} - -.btn-group-lg>.btn, .btn-lg { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px -} - -.btn-group-sm>.btn, .btn-sm { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px -} - -.btn-group-xs>.btn, .btn-xs { - padding: 1px 5px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px -} - -.btn-block { - display: block; - width: 100% -} - -.btn-block+.btn-block { - margin-top: 5px -} - -input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].btn-block - { - width: 100% -} - -.fade { - opacity: 0; - -webkit-transition: opacity .15s linear; - -o-transition: opacity .15s linear; - transition: opacity .15s linear -} - -.fade.in { - opacity: 1 -} - -.collapse { - display: none -} - -.collapse.in { - display: block -} - -tr.collapse.in { - display: table-row -} - -tbody.collapse.in { - display: table-row-group -} - -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition-timing-function: ease; - -o-transition-timing-function: ease; - transition-timing-function: ease; - -webkit-transition-duration: .35s; - -o-transition-duration: .35s; - transition-duration: .35s; - -webkit-transition-property: height, visibility; - -o-transition-property: height, visibility; - transition-property: height, visibility -} - -.caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 2px; - vertical-align: middle; - border-top: 4px dashed; - border-right: 4px solid transparent; - border-left: 4px solid transparent -} - -.dropdown, .dropup { - position: relative -} - -.dropdown-toggle:focus { - outline: 0 -} - -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - font-size: 14px; - text-align: left; - list-style: none; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); - box-shadow: 0 6px 12px rgba(0, 0, 0, .175) -} - -.dropdown-menu.pull-right { - right: 0; - left: auto -} - -.dropdown-menu .divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5 -} - -.dropdown-menu>li>a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: 400; - line-height: 1.42857143; - color: #333; - white-space: nowrap -} - -.dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover { - color: #262626; - text-decoration: none; - background-color: #f5f5f5 -} - -.dropdown-menu>.active>a, .dropdown-menu>.active>a:focus, .dropdown-menu>.active>a:hover - { - color: #fff; - text-decoration: none; - background-color: #337ab7; - outline: 0 -} - -.dropdown-menu>.disabled>a, .dropdown-menu>.disabled>a:focus, - .dropdown-menu>.disabled>a:hover { - color: #777 -} - -.dropdown-menu>.disabled>a:focus, .dropdown-menu>.disabled>a:hover { - text-decoration: none; - cursor: not-allowed; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false) -} - -.open>.dropdown-menu { - display: block -} - -.open>a { - outline: 0 -} - -.dropdown-menu-right { - right: 0; - left: auto -} - -.dropdown-menu-left { - right: auto; - left: 0 -} - -.dropdown-header { - display: block; - padding: 3px 20px; - font-size: 12px; - line-height: 1.42857143; - color: #777; - white-space: nowrap -} - -.dropdown-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 990 -} - -.pull-right>.dropdown-menu { - right: 0; - left: auto -} - -.dropup .caret, .navbar-fixed-bottom .dropdown .caret { - content: ""; - border-top: 0; - border-bottom: 4px solid -} - -.dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 2px -} - -@media ( min-width :768px) { - .navbar-right .dropdown-menu { - right: 0; - left: auto - } - .navbar-right .dropdown-menu-left { - right: auto; - left: 0 - } -} - -.btn-group, .btn-group-vertical { - position: relative; - display: inline-block; - vertical-align: middle -} - -.btn-group-vertical>.btn, .btn-group>.btn { - position: relative; - float: left -} - -.btn-group-vertical>.btn.active, .btn-group-vertical>.btn:active, - .btn-group-vertical>.btn:focus, .btn-group-vertical>.btn:hover, - .btn-group>.btn.active, .btn-group>.btn:active, .btn-group>.btn:focus, - .btn-group>.btn:hover { - z-index: 2 -} - -.btn-group .btn+.btn, .btn-group .btn+.btn-group, .btn-group .btn-group+.btn, - .btn-group .btn-group+.btn-group { - margin-left: -1px -} - -.btn-toolbar { - margin-left: -5px -} - -.btn-toolbar .btn-group, .btn-toolbar .input-group { - float: left -} - -.btn-toolbar>.btn, .btn-toolbar>.btn-group, .btn-toolbar>.input-group { - margin-left: 5px -} - -.btn-group>.btn:not (:first-child ):not (:last-child ):not (.dropdown-toggle - ){ - border-radius: 0 -} - -.btn-group>.btn:first-child { - margin-left: 0 -} - -.btn-group>.btn:first-child:not (:last-child ):not (.dropdown-toggle ){ - border-top-right-radius: 0; - border-bottom-right-radius: 0 -} - -.btn-group>.btn:last-child:not (:first-child ), .btn-group>.dropdown-toggle:not - (:first-child ){ - border-top-left-radius: 0; - border-bottom-left-radius: 0 -} - -.btn-group>.btn-group { - float: left -} - -.btn-group>.btn-group:not (:first-child ):not (:last-child )>.btn { - border-radius: 0 -} - -.btn-group>.btn-group:first-child:not (:last-child )>.btn:last-child, - .btn-group>.btn-group:first-child:not (:last-child )>.dropdown-toggle { - border-top-right-radius: 0; - border-bottom-right-radius: 0 -} - -.btn-group>.btn-group:last-child:not (:first-child )>.btn:first-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0 -} - -.btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { - outline: 0 -} - -.btn-group>.btn+.dropdown-toggle { - padding-right: 8px; - padding-left: 8px -} - -.btn-group>.btn-lg+.dropdown-toggle { - padding-right: 12px; - padding-left: 12px -} - -.btn-group.open .dropdown-toggle { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125) -} - -.btn-group.open .dropdown-toggle.btn-link { - -webkit-box-shadow: none; - box-shadow: none -} - -.btn .caret { - margin-left: 0 -} - -.btn-lg .caret { - border-width: 5px 5px 0; - border-bottom-width: 0 -} - -.dropup .btn-lg .caret { - border-width: 0 5px 5px -} - -.btn-group-vertical>.btn, .btn-group-vertical>.btn-group, - .btn-group-vertical>.btn-group>.btn { - display: block; - float: none; - width: 100%; - max-width: 100% -} - -.btn-group-vertical>.btn-group>.btn { - float: none -} - -.btn-group-vertical>.btn+.btn, .btn-group-vertical>.btn+.btn-group, - .btn-group-vertical>.btn-group+.btn, .btn-group-vertical>.btn-group+.btn-group - { - margin-top: -1px; - margin-left: 0 -} - -.btn-group-vertical>.btn:not (:first-child ):not (:last-child ){ - border-radius: 0 -} - -.btn-group-vertical>.btn:first-child:not (:last-child ){ - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0 -} - -.btn-group-vertical>.btn:last-child:not (:first-child ){ - border-top-left-radius: 0; - border-top-right-radius: 0; - border-bottom-left-radius: 4px -} - -.btn-group-vertical>.btn-group:not (:first-child ):not (:last-child )>.btn - { - border-radius: 0 -} - -.btn-group-vertical>.btn-group:first-child:not (:last-child )>.btn:last-child, - .btn-group-vertical>.btn-group:first-child:not (:last-child )>.dropdown-toggle - { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0 -} - -.btn-group-vertical>.btn-group:last-child:not (:first-child )>.btn:first-child - { - border-top-left-radius: 0; - border-top-right-radius: 0 -} - -.btn-group-justified { - display: table; - width: 100%; - table-layout: fixed; - border-collapse: separate -} - -.btn-group-justified>.btn, .btn-group-justified>.btn-group { - display: table-cell; - float: none; - width: 1% -} - -.btn-group-justified>.btn-group .btn { - width: 100% -} - -.btn-group-justified>.btn-group .dropdown-menu { - left: auto -} - -[data-toggle=buttons]>.btn input[type=checkbox], [data-toggle=buttons]>.btn input[type=radio], - [data-toggle=buttons]>.btn-group>.btn input[type=checkbox], [data-toggle=buttons]>.btn-group>.btn input[type=radio] - { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none -} - -.input-group { - position: relative; - display: table; - border-collapse: separate -} - -.input-group[class*=col-] { - float: none; - padding-right: 0; - padding-left: 0 -} - -.input-group .form-control { - position: relative; - z-index: 2; - float: left; - width: 100%; - margin-bottom: 0 -} - -.input-group-lg>.form-control, .input-group-lg>.input-group-addon, - .input-group-lg>.input-group-btn>.btn { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px -} - -select.input-group-lg>.form-control, select.input-group-lg>.input-group-addon, - select.input-group-lg>.input-group-btn>.btn { - height: 46px; - line-height: 46px -} - -select[multiple].input-group-lg>.form-control, select[multiple].input-group-lg>.input-group-addon, - select[multiple].input-group-lg>.input-group-btn>.btn, textarea.input-group-lg>.form-control, - textarea.input-group-lg>.input-group-addon, textarea.input-group-lg>.input-group-btn>.btn - { - height: auto -} - -.input-group-sm>.form-control, .input-group-sm>.input-group-addon, - .input-group-sm>.input-group-btn>.btn { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px -} - -select.input-group-sm>.form-control, select.input-group-sm>.input-group-addon, - select.input-group-sm>.input-group-btn>.btn { - height: 30px; - line-height: 30px -} - -select[multiple].input-group-sm>.form-control, select[multiple].input-group-sm>.input-group-addon, - select[multiple].input-group-sm>.input-group-btn>.btn, textarea.input-group-sm>.form-control, - textarea.input-group-sm>.input-group-addon, textarea.input-group-sm>.input-group-btn>.btn - { - height: auto -} - -.input-group .form-control, .input-group-addon, .input-group-btn { - display: table-cell -} - -.input-group .form-control:not (:first-child ):not (:last-child ), - .input-group-addon:not (:first-child ):not (:last-child ), - .input-group-btn:not (:first-child ):not (:last-child ){ - border-radius: 0 -} - -.input-group-addon, .input-group-btn { - width: 1%; - white-space: nowrap; - vertical-align: middle -} - -.input-group-addon { - padding: 6px 12px; - font-size: 14px; - font-weight: 400; - line-height: 1; - color: #555; - text-align: center; - background-color: #eee; - border: 1px solid #ccc; - border-radius: 4px -} - -.input-group-addon.input-sm { - padding: 5px 10px; - font-size: 12px; - border-radius: 3px -} - -.input-group-addon.input-lg { - padding: 10px 16px; - font-size: 18px; - border-radius: 6px -} - -.input-group-addon input[type=checkbox], .input-group-addon input[type=radio] - { - margin-top: 0 -} - -.input-group .form-control:first-child, .input-group-addon:first-child, - .input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group>.btn, - .input-group-btn:first-child>.dropdown-toggle, .input-group-btn:last-child>.btn-group:not - (:last-child )>.btn, .input-group-btn:last-child>.btn:not (:last-child - ):not (.dropdown-toggle ){ - border-top-right-radius: 0; - border-bottom-right-radius: 0 -} - -.input-group-addon:first-child { - border-right: 0 -} - -.input-group .form-control:last-child, .input-group-addon:last-child, - .input-group-btn:first-child>.btn-group:not (:first-child )>.btn, - .input-group-btn:first-child>.btn:not (:first-child ), .input-group-btn:last-child>.btn, - .input-group-btn:last-child>.btn-group>.btn, .input-group-btn:last-child>.dropdown-toggle - { - border-top-left-radius: 0; - border-bottom-left-radius: 0 -} - -.input-group-addon:last-child { - border-left: 0 -} - -.input-group-btn { - position: relative; - font-size: 0; - white-space: nowrap -} - -.input-group-btn>.btn { - position: relative -} - -.input-group-btn>.btn+.btn { - margin-left: -1px -} - -.input-group-btn>.btn:active, .input-group-btn>.btn:focus, - .input-group-btn>.btn:hover { - z-index: 2 -} - -.input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group - { - margin-right: -1px -} - -.input-group-btn:last-child>.btn, .input-group-btn:last-child>.btn-group - { - margin-left: -1px -} - -.nav { - padding-left: 0; - margin-bottom: 0; - list-style: none -} - -.nav>li { - position: relative; - display: block -} - -.nav>li>a { - position: relative; - display: block; - padding: 10px 15px -} - -.nav>li>a:focus, .nav>li>a:hover { - text-decoration: none; - background-color: #eee -} - -.nav>li.disabled>a { - color: #777 -} - -.nav>li.disabled>a:focus, .nav>li.disabled>a:hover { - color: #777; - text-decoration: none; - cursor: not-allowed; - background-color: transparent -} - -.nav .open>a, .nav .open>a:focus, .nav .open>a:hover { - background-color: #eee; - border-color: #337ab7 -} - -.nav .nav-divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5 -} - -.nav>li>a>img { - max-width: none -} - -.nav-tabs { - border-bottom: 1px solid #ddd -} - -.nav-tabs>li { - float: left; - margin-bottom: -1px -} - -.nav-tabs>li>a { - margin-right: 2px; - line-height: 1.42857143; - border: 1px solid transparent; - border-radius: 4px 4px 0 0 -} - -.nav-tabs>li>a:hover { - border-color: #eee #eee #ddd -} - -.nav-tabs>li.active>a, .nav-tabs>li.active>a:focus, .nav-tabs>li.active>a:hover - { - color: #555; - cursor: default; - background-color: #fff; - border: 1px solid #ddd; - border-bottom-color: transparent -} - -.nav-tabs.nav-justified { - width: 100%; - border-bottom: 0 -} - -.nav-tabs.nav-justified>li { - float: none -} - -.nav-tabs.nav-justified>li>a { - margin-bottom: 5px; - text-align: center -} - -.nav-tabs.nav-justified>.dropdown .dropdown-menu { - top: auto; - left: auto -} - -@media ( min-width :768px) { - .nav-tabs.nav-justified>li { - display: table-cell; - width: 1% - } - .nav-tabs.nav-justified>li>a { - margin-bottom: 0 - } -} - -.nav-tabs.nav-justified>li>a { - margin-right: 0; - border-radius: 4px -} - -.nav-tabs.nav-justified>.active>a, .nav-tabs.nav-justified>.active>a:focus, - .nav-tabs.nav-justified>.active>a:hover { - border: 1px solid #ddd -} - -@media ( min-width :768px) { - .nav-tabs.nav-justified>li>a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0 - } - .nav-tabs.nav-justified>.active>a, .nav-tabs.nav-justified>.active>a:focus, - .nav-tabs.nav-justified>.active>a:hover { - border-bottom-color: #fff - } -} - -.nav-pills>li { - float: left -} - -.nav-pills>li>a { - border-radius: 4px -} - -.nav-pills>li+li { - margin-left: 2px -} - -.nav-pills>li.active>a, .nav-pills>li.active>a:focus, .nav-pills>li.active>a:hover - { - color: #fff; - background-color: #337ab7 -} - -.nav-stacked>li { - float: none -} - -.nav-stacked>li+li { - margin-top: 2px; - margin-left: 0 -} - -.nav-justified { - width: 100% -} - -.nav-justified>li { - float: none -} - -.nav-justified>li>a { - margin-bottom: 5px; - text-align: center -} - -.nav-justified>.dropdown .dropdown-menu { - top: auto; - left: auto -} - -@media ( min-width :768px) { - .nav-justified>li { - display: table-cell; - width: 1% - } - .nav-justified>li>a { - margin-bottom: 0 - } -} - -.nav-tabs-justified { - border-bottom: 0 -} - -.nav-tabs-justified>li>a { - margin-right: 0; - border-radius: 4px -} - -.nav-tabs-justified>.active>a, .nav-tabs-justified>.active>a:focus, - .nav-tabs-justified>.active>a:hover { - border: 1px solid #ddd -} - -@media ( min-width :768px) { - .nav-tabs-justified>li>a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0 - } - .nav-tabs-justified>.active>a, .nav-tabs-justified>.active>a:focus, - .nav-tabs-justified>.active>a:hover { - border-bottom-color: #fff - } -} - -.tab-content>.tab-pane { - display: none -} - -.tab-content>.active { - display: block -} - -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0 -} - -.navbar { - position: relative; - min-height: 50px; - margin-bottom: 20px; - border: 1px solid transparent -} - -@media ( min-width :768px) { - .navbar { - border-radius: 4px - } -} - -@media ( min-width :768px) { - .navbar-header { - float: left - } -} - -.navbar-collapse { - padding-right: 15px; - padding-left: 15px; - overflow-x: visible; - -webkit-overflow-scrolling: touch; - border-top: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1) -} - -.navbar-collapse.in { - overflow-y: auto -} - -@media ( min-width :768px) { - .navbar-collapse { - width: auto; - border-top: 0; - -webkit-box-shadow: none; - box-shadow: none - } - .navbar-collapse.collapse { - display: block !important; - height: auto !important; - padding-bottom: 0; - overflow: visible !important - } - .navbar-collapse.in { - overflow-y: visible - } - .navbar-fixed-bottom .navbar-collapse, .navbar-fixed-top .navbar-collapse, - .navbar-static-top .navbar-collapse { - padding-right: 0; - padding-left: 0 - } -} - -.navbar-fixed-bottom .navbar-collapse, .navbar-fixed-top .navbar-collapse - { - max-height: 340px -} - -@media ( max-device-width :480px)and (orientation:landscape) { - .navbar-fixed-bottom .navbar-collapse, .navbar-fixed-top .navbar-collapse - { - max-height: 200px - } -} - -.container-fluid>.navbar-collapse, .container-fluid>.navbar-header, - .container>.navbar-collapse, .container>.navbar-header { - margin-right: -15px; - margin-left: -15px -} - -@media ( min-width :768px) { - .container-fluid>.navbar-collapse, .container-fluid>.navbar-header, - .container>.navbar-collapse, .container>.navbar-header { - margin-right: 0; - margin-left: 0 - } -} - -.navbar-static-top { - z-index: 1000; - border-width: 0 0 1px -} - -@media ( min-width :768px) { - .navbar-static-top { - border-radius: 0 - } -} - -.navbar-fixed-bottom, .navbar-fixed-top { - position: fixed; - right: 0; - left: 0; - z-index: 1030 -} - -@media ( min-width :768px) { - .navbar-fixed-bottom, .navbar-fixed-top { - border-radius: 0 - } -} - -.navbar-fixed-top { - top: 0; - border-width: 0 0 1px -} - -.navbar-fixed-bottom { - bottom: 0; - margin-bottom: 0; - border-width: 1px 0 0 -} - -.navbar-brand { - float: left; - height: 50px; - padding: 15px 15px; - font-size: 18px; - line-height: 20px -} - -.navbar-brand:focus, .navbar-brand:hover { - text-decoration: none -} - -.navbar-brand>img { - display: block -} - -@media ( min-width :768px) { - .navbar>.container .navbar-brand, .navbar>.container-fluid .navbar-brand - { - margin-left: -15px - } -} - -.navbar-toggle { - position: relative; - float: right; - padding: 9px 10px; - margin-top: 8px; - margin-right: 15px; - margin-bottom: 8px; - background-color: transparent; - background-image: none; - border: 1px solid transparent; - border-radius: 4px -} - -.navbar-toggle:focus { - outline: 0 -} - -.navbar-toggle .icon-bar { - display: block; - width: 22px; - height: 2px; - border-radius: 1px -} - -.navbar-toggle .icon-bar+.icon-bar { - margin-top: 4px -} - -@media ( min-width :768px) { - .navbar-toggle { - display: none - } -} - -.navbar-nav { - margin: 7.5px -15px -} - -.navbar-nav>li>a { - padding-top: 10px; - padding-bottom: 10px; - line-height: 20px -} - -@media ( max-width :767px) { - .navbar-nav .open .dropdown-menu { - position: static; - float: none; - width: auto; - margin-top: 0; - background-color: transparent; - border: 0; - -webkit-box-shadow: none; - box-shadow: none - } - .navbar-nav .open .dropdown-menu .dropdown-header, .navbar-nav .open .dropdown-menu>li>a - { - padding: 5px 15px 5px 25px - } - .navbar-nav .open .dropdown-menu>li>a { - line-height: 20px - } - .navbar-nav .open .dropdown-menu>li>a:focus, .navbar-nav .open .dropdown-menu>li>a:hover - { - background-image: none - } -} - -@media ( min-width :768px) { - .navbar-nav { - float: left; - margin: 0 - } - .navbar-nav>li { - float: left - } - .navbar-nav>li>a { - padding-top: 15px; - padding-bottom: 15px - } -} - -.navbar-form { - padding: 10px 15px; - margin-top: 8px; - margin-right: -15px; - margin-bottom: 8px; - margin-left: -15px; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 - rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 - rgba(255, 255, 255, .1) -} - -@media ( min-width :768px) { - .navbar-form .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle - } - .navbar-form .form-control { - display: inline-block; - width: auto; - vertical-align: middle - } - .navbar-form .form-control-static { - display: inline-block - } - .navbar-form .input-group { - display: inline-table; - vertical-align: middle - } - .navbar-form .input-group .form-control, .navbar-form .input-group .input-group-addon, - .navbar-form .input-group .input-group-btn { - width: auto - } - .navbar-form .input-group>.form-control { - width: 100% - } - .navbar-form .control-label { - margin-bottom: 0; - vertical-align: middle - } - .navbar-form .checkbox, .navbar-form .radio { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle - } - .navbar-form .checkbox label, .navbar-form .radio label { - padding-left: 0 - } - .navbar-form .checkbox input[type=checkbox], .navbar-form .radio input[type=radio] - { - position: relative; - margin-left: 0 - } - .navbar-form .has-feedback .form-control-feedback { - top: 0 - } -} - -@media ( max-width :767px) { - .navbar-form .form-group { - margin-bottom: 5px - } - .navbar-form .form-group:last-child { - margin-bottom: 0 - } -} - -@media ( min-width :768px) { - .navbar-form { - width: auto; - padding-top: 0; - padding-bottom: 0; - margin-right: 0; - margin-left: 0; - border: 0; - -webkit-box-shadow: none; - box-shadow: none - } -} - -.navbar-nav>li>.dropdown-menu { - margin-top: 0; - border-top-left-radius: 0; - border-top-right-radius: 0 -} - -.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu { - margin-bottom: 0; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0 -} - -.navbar-btn { - margin-top: 8px; - margin-bottom: 8px -} - -.navbar-btn.btn-sm { - margin-top: 10px; - margin-bottom: 10px -} - -.navbar-btn.btn-xs { - margin-top: 14px; - margin-bottom: 14px -} - -.navbar-text { - margin-top: 15px; - margin-bottom: 15px -} - -@media ( min-width :768px) { - .navbar-text { - float: left; - margin-right: 15px; - margin-left: 15px - } -} - -@media ( min-width :768px) { - .navbar-left { - float: left !important - } - .navbar-right { - float: right !important; - margin-right: -15px - } - .navbar-right ~.navbar-right { - margin-right: 0 - } -} - -.navbar-default { - background-color: #f8f8f8; - border-color: #e7e7e7 -} - -.navbar-default .navbar-brand { - color: #777 -} - -.navbar-default .navbar-brand:focus, .navbar-default .navbar-brand:hover - { - color: #5e5e5e; - background-color: transparent -} - -.navbar-default .navbar-text { - color: #777 -} - -.navbar-default .navbar-nav>li>a { - color: #777 -} - -.navbar-default .navbar-nav>li>a:focus, .navbar-default .navbar-nav>li>a:hover - { - color: #333; - background-color: transparent -} - -.navbar-default .navbar-nav>.active>a, .navbar-default .navbar-nav>.active>a:focus, - .navbar-default .navbar-nav>.active>a:hover { - color: #555; - background-color: #e7e7e7 -} - -.navbar-default .navbar-nav>.disabled>a, .navbar-default .navbar-nav>.disabled>a:focus, - .navbar-default .navbar-nav>.disabled>a:hover { - color: #ccc; - background-color: transparent -} - -.navbar-default .navbar-toggle { - border-color: #ddd -} - -.navbar-default .navbar-toggle:focus, .navbar-default .navbar-toggle:hover - { - background-color: #ddd -} - -.navbar-default .navbar-toggle .icon-bar { - background-color: #888 -} - -.navbar-default .navbar-collapse, .navbar-default .navbar-form { - border-color: #e7e7e7 -} - -.navbar-default .navbar-nav>.open>a, .navbar-default .navbar-nav>.open>a:focus, - .navbar-default .navbar-nav>.open>a:hover { - color: #555; - background-color: #e7e7e7 -} - -@media ( max-width :767px) { - .navbar-default .navbar-nav .open .dropdown-menu>li>a { - color: #777 - } - .navbar-default .navbar-nav .open .dropdown-menu>li>a:focus, - .navbar-default .navbar-nav .open .dropdown-menu>li>a:hover { - color: #333; - background-color: transparent - } - .navbar-default .navbar-nav .open .dropdown-menu>.active>a, - .navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus, - .navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover { - color: #555; - background-color: #e7e7e7 - } - .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a, - .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus, - .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover { - color: #ccc; - background-color: transparent - } -} - -.navbar-default .navbar-link { - color: #777 -} - -.navbar-default .navbar-link:hover { - color: #333 -} - -.navbar-default .btn-link { - color: #777 -} - -.navbar-default .btn-link:focus, .navbar-default .btn-link:hover { - color: #333 -} - -.navbar-default .btn-link[disabled]:focus, .navbar-default .btn-link[disabled]:hover, - fieldset[disabled] .navbar-default .btn-link:focus, fieldset[disabled] .navbar-default .btn-link:hover - { - color: #ccc -} - -.navbar-inverse { - background-color: #222; - border-color: #080808 -} - -.navbar-inverse .navbar-brand { - color: #9d9d9d -} - -.navbar-inverse .navbar-brand:focus, .navbar-inverse .navbar-brand:hover - { - color: #fff; - background-color: transparent -} - -.navbar-inverse .navbar-text { - color: #9d9d9d -} - -.navbar-inverse .navbar-nav>li>a { - color: #9d9d9d -} - -.navbar-inverse .navbar-nav>li>a:focus, .navbar-inverse .navbar-nav>li>a:hover - { - color: #fff; - background-color: transparent -} - -.navbar-inverse .navbar-nav>.active>a, .navbar-inverse .navbar-nav>.active>a:focus, - .navbar-inverse .navbar-nav>.active>a:hover { - color: #fff; - background-color: #080808 -} - -.navbar-inverse .navbar-nav>.disabled>a, .navbar-inverse .navbar-nav>.disabled>a:focus, - .navbar-inverse .navbar-nav>.disabled>a:hover { - color: #444; - background-color: transparent -} - -.navbar-inverse .navbar-toggle { - border-color: #333 -} - -.navbar-inverse .navbar-toggle:focus, .navbar-inverse .navbar-toggle:hover - { - background-color: #333 -} - -.navbar-inverse .navbar-toggle .icon-bar { - background-color: #fff -} - -.navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { - border-color: #101010 -} - -.navbar-inverse .navbar-nav>.open>a, .navbar-inverse .navbar-nav>.open>a:focus, - .navbar-inverse .navbar-nav>.open>a:hover { - color: #fff; - background-color: #080808 -} - -@media ( max-width :767px) { - .navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header { - border-color: #080808 - } - .navbar-inverse .navbar-nav .open .dropdown-menu .divider { - background-color: #080808 - } - .navbar-inverse .navbar-nav .open .dropdown-menu>li>a { - color: #9d9d9d - } - .navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus, - .navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover { - color: #fff; - background-color: transparent - } - .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a, - .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus, - .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover { - color: #fff; - background-color: #080808 - } - .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a, - .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus, - .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover { - color: #444; - background-color: transparent - } -} - -.navbar-inverse .navbar-link { - color: #9d9d9d -} - -.navbar-inverse .navbar-link:hover { - color: #fff -} - -.navbar-inverse .btn-link { - color: #9d9d9d -} - -.navbar-inverse .btn-link:focus, .navbar-inverse .btn-link:hover { - color: #fff -} - -.navbar-inverse .btn-link[disabled]:focus, .navbar-inverse .btn-link[disabled]:hover, - fieldset[disabled] .navbar-inverse .btn-link:focus, fieldset[disabled] .navbar-inverse .btn-link:hover - { - color: #444 -} - -.breadcrumb { - padding: 8px 15px; - margin-bottom: 20px; - list-style: none; - background-color: #f5f5f5; - border-radius: 4px -} - -.breadcrumb>li { - display: inline-block -} - -.breadcrumb>li+li:before { - padding: 0 5px; - color: #ccc; - content: "/\00a0" -} - -.breadcrumb>.active { - color: #777 -} - -.pagination { - display: inline-block; - padding-left: 0; - margin: 20px 0; - border-radius: 4px -} - -.pagination>li { - display: inline -} - -.pagination>li>a, .pagination>li>span { - position: relative; - float: left; - padding: 6px 12px; - margin-left: -1px; - line-height: 1.42857143; - color: #337ab7; - text-decoration: none; - background-color: #fff; - border: 1px solid #ddd -} - -.pagination>li:first-child>a, .pagination>li:first-child>span { - margin-left: 0; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px -} - -.pagination>li:last-child>a, .pagination>li:last-child>span { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px -} - -.pagination>li>a:focus, .pagination>li>a:hover, .pagination>li>span:focus, - .pagination>li>span:hover { - color: #23527c; - background-color: #eee; - border-color: #ddd -} - -.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover, - .pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover - { - z-index: 2; - color: #fff; - cursor: default; - background-color: #337ab7; - border-color: #337ab7 -} - -.pagination>.disabled>a, .pagination>.disabled>a:focus, .pagination>.disabled>a:hover, - .pagination>.disabled>span, .pagination>.disabled>span:focus, - .pagination>.disabled>span:hover { - color: #777; - cursor: not-allowed; - background-color: #fff; - border-color: #ddd -} - -.pagination-lg>li>a, .pagination-lg>li>span { - padding: 10px 16px; - font-size: 18px -} - -.pagination-lg>li:first-child>a, .pagination-lg>li:first-child>span { - border-top-left-radius: 6px; - border-bottom-left-radius: 6px -} - -.pagination-lg>li:last-child>a, .pagination-lg>li:last-child>span { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px -} - -.pagination-sm>li>a, .pagination-sm>li>span { - padding: 5px 10px; - font-size: 12px -} - -.pagination-sm>li:first-child>a, .pagination-sm>li:first-child>span { - border-top-left-radius: 3px; - border-bottom-left-radius: 3px -} - -.pagination-sm>li:last-child>a, .pagination-sm>li:last-child>span { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px -} - -.pager { - padding-left: 0; - margin: 20px 0; - text-align: center; - list-style: none -} - -.pager li { - display: inline -} - -.pager li>a, .pager li>span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 15px -} - -.pager li>a:focus, .pager li>a:hover { - text-decoration: none; - background-color: #eee -} - -.pager .next>a, .pager .next>span { - float: right -} - -.pager .previous>a, .pager .previous>span { - float: left -} - -.pager .disabled>a, .pager .disabled>a:focus, .pager .disabled>a:hover, - .pager .disabled>span { - color: #777; - cursor: not-allowed; - background-color: #fff -} - -.label { - display: inline; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: 700; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em -} - -a.label:focus, a.label:hover { - color: #fff; - text-decoration: none; - cursor: pointer -} - -.label:empty { - display: none -} - -.btn .label { - position: relative; - top: -1px -} - -.label-default { - background-color: #777 -} - -.label-default[href]:focus, .label-default[href]:hover { - background-color: #5e5e5e -} - -.label-primary { - background-color: #337ab7 -} - -.label-primary[href]:focus, .label-primary[href]:hover { - background-color: #286090 -} - -.label-success { - background-color: #5cb85c -} - -.label-success[href]:focus, .label-success[href]:hover { - background-color: #449d44 -} - -.label-info { - background-color: #5bc0de -} - -.label-info[href]:focus, .label-info[href]:hover { - background-color: #31b0d5 -} - -.label-warning { - background-color: #f0ad4e -} - -.label-warning[href]:focus, .label-warning[href]:hover { - background-color: #ec971f -} - -.label-danger { - background-color: #d9534f -} - -.label-danger[href]:focus, .label-danger[href]:hover { - background-color: #c9302c -} - -.badge { - display: inline-block; - min-width: 10px; - padding: 3px 7px; - font-size: 12px; - font-weight: 700; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - background-color: #777; - border-radius: 10px -} - -.badge:empty { - display: none -} - -.btn .badge { - position: relative; - top: -1px -} - -.btn-group-xs>.btn .badge, .btn-xs .badge { - top: 0; - padding: 1px 5px -} - -a.badge:focus, a.badge:hover { - color: #fff; - text-decoration: none; - cursor: pointer -} - -.list-group-item.active>.badge, .nav-pills>.active>a>.badge { - color: #337ab7; - background-color: #fff -} - -.list-group-item>.badge { - float: right -} - -.list-group-item>.badge+.badge { - margin-right: 5px -} - -.nav-pills>li>a>.badge { - margin-left: 3px -} - -.jumbotron { - padding: 30px 15px; - margin-bottom: 30px; - color: inherit; - background-color: #eee -} - -.jumbotron .h1, .jumbotron h1 { - color: inherit -} - -.jumbotron p { - margin-bottom: 15px; - font-size: 21px; - font-weight: 200 -} - -.jumbotron>hr { - border-top-color: #d5d5d5 -} - -.container .jumbotron, .container-fluid .jumbotron { - border-radius: 6px -} - -.jumbotron .container { - max-width: 100% -} - -@media screen and (min-width:768px) { - .jumbotron { - padding: 48px 0 - } - .container .jumbotron, .container-fluid .jumbotron { - padding-right: 60px; - padding-left: 60px - } - .jumbotron .h1, .jumbotron h1 { - font-size: 63px - } -} - -.thumbnail { - display: block; - padding: 4px; - margin-bottom: 20px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: border .2s ease-in-out; - -o-transition: border .2s ease-in-out; - transition: border .2s ease-in-out -} - -.thumbnail a>img, .thumbnail>img { - margin-right: auto; - margin-left: auto -} - -a.thumbnail.active, a.thumbnail:focus, a.thumbnail:hover { - border-color: #337ab7 -} - -.thumbnail .caption { - padding: 9px; - color: #333 -} - -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px -} - -.alert h4 { - margin-top: 0; - color: inherit -} - -.alert .alert-link { - font-weight: 700 -} - -.alert>p, .alert>ul { - margin-bottom: 0 -} - -.alert>p+p { - margin-top: 5px -} - -.alert-dismissable, .alert-dismissible { - padding-right: 35px -} - -.alert-dismissable .close, .alert-dismissible .close { - position: relative; - top: -2px; - right: -21px; - color: inherit -} - -.alert-success { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6 -} - -.alert-success hr { - border-top-color: #c9e2b3 -} - -.alert-success .alert-link { - color: #2b542c -} - -.alert-info { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1 -} - -.alert-info hr { - border-top-color: #a6e1ec -} - -.alert-info .alert-link { - color: #245269 -} - -.alert-warning { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc -} - -.alert-warning hr { - border-top-color: #f7e1b5 -} - -.alert-warning .alert-link { - color: #66512c -} - -.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1 -} - -.alert-danger hr { - border-top-color: #e4b9c0 -} - -.alert-danger .alert-link { - color: #843534 -} - -@ --webkit-keyframes progress-bar-stripes { - from {background-position: 40px 0 -} - -to { - background-position: 0 0 -} - -} -@ --o-keyframes progress-bar-stripes { - from {background-position: 40px 0 -} - -to { - background-position: 0 0 -} - -} -@ -keyframes progress-bar-stripes { - from {background-position: 40px 0 -} - -to { - background-position: 0 0 -} - -} -.progress { - height: 20px; - margin-bottom: 20px; - overflow: hidden; - background-color: #f5f5f5; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1) -} - -.progress-bar { - float: left; - width: 0; - height: 100%; - font-size: 12px; - line-height: 20px; - color: #fff; - text-align: center; - background-color: #337ab7; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - -webkit-transition: width .6s ease; - -o-transition: width .6s ease; - transition: width .6s ease -} - -.progress-bar-striped, .progress-striped .progress-bar { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) - 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, - transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, - transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - background-size: 40px 40px -} - -.progress-bar.active, .progress.active .progress-bar { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite -} - -.progress-bar-success { - background-color: #5cb85c -} - -.progress-striped .progress-bar-success { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) - 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, - transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, - transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent) -} - -.progress-bar-info { - background-color: #5bc0de -} - -.progress-striped .progress-bar-info { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) - 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, - transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, - transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent) -} - -.progress-bar-warning { - background-color: #f0ad4e -} - -.progress-striped .progress-bar-warning { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) - 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, - transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, - transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent) -} - -.progress-bar-danger { - background-color: #d9534f -} - -.progress-striped .progress-bar-danger { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) - 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, - transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, - transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, - rgba(255, 255, 255, .15) 75%, transparent 75%, transparent) -} - -.media { - margin-top: 15px -} - -.media:first-child { - margin-top: 0 -} - -.media, .media-body { - overflow: hidden; - zoom: 1 -} - -.media-body { - width: 10000px -} - -.media-object { - display: block -} - -.media-right, .media>.pull-right { - padding-left: 10px -} - -.media-left, .media>.pull-left { - padding-right: 10px -} - -.media-body, .media-left, .media-right { - display: table-cell; - vertical-align: top -} - -.media-middle { - vertical-align: middle -} - -.media-bottom { - vertical-align: bottom -} - -.media-heading { - margin-top: 0; - margin-bottom: 5px -} - -.media-list { - padding-left: 0; - list-style: none -} - -.list-group { - padding-left: 0; - margin-bottom: 20px -} - -.list-group-item { - position: relative; - display: block; - padding: 10px 15px; - margin-bottom: -1px; - background-color: #fff; - border: 1px solid #ddd -} - -.list-group-item:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px -} - -.list-group-item:last-child { - margin-bottom: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px -} - -a.list-group-item { - color: #555 -} - -a.list-group-item .list-group-item-heading { - color: #333 -} - -a.list-group-item:focus, a.list-group-item:hover { - color: #555; - text-decoration: none; - background-color: #f5f5f5 -} - -.list-group-item.disabled, .list-group-item.disabled:focus, - .list-group-item.disabled:hover { - color: #777; - cursor: not-allowed; - background-color: #eee -} - -.list-group-item.disabled .list-group-item-heading, .list-group-item.disabled:focus .list-group-item-heading, - .list-group-item.disabled:hover .list-group-item-heading { - color: inherit -} - -.list-group-item.disabled .list-group-item-text, .list-group-item.disabled:focus .list-group-item-text, - .list-group-item.disabled:hover .list-group-item-text { - color: #777 -} - -.list-group-item.active, .list-group-item.active:focus, .list-group-item.active:hover - { - z-index: 2; - color: #fff; - background-color: #337ab7; - border-color: #337ab7 -} - -.list-group-item.active .list-group-item-heading, .list-group-item.active .list-group-item-heading>.small, - .list-group-item.active .list-group-item-heading>small, - .list-group-item.active:focus .list-group-item-heading, - .list-group-item.active:focus .list-group-item-heading>.small, - .list-group-item.active:focus .list-group-item-heading>small, - .list-group-item.active:hover .list-group-item-heading, - .list-group-item.active:hover .list-group-item-heading>.small, - .list-group-item.active:hover .list-group-item-heading>small { - color: inherit -} - -.list-group-item.active .list-group-item-text, .list-group-item.active:focus .list-group-item-text, - .list-group-item.active:hover .list-group-item-text { - color: #c7ddef -} - -.list-group-item-success { - color: #3c763d; - background-color: #dff0d8 -} - -a.list-group-item-success { - color: #3c763d -} - -a.list-group-item-success .list-group-item-heading { - color: inherit -} - -a.list-group-item-success:focus, a.list-group-item-success:hover { - color: #3c763d; - background-color: #d0e9c6 -} - -a.list-group-item-success.active, a.list-group-item-success.active:focus, - a.list-group-item-success.active:hover { - color: #fff; - background-color: #3c763d; - border-color: #3c763d -} - -.list-group-item-info { - color: #31708f; - background-color: #d9edf7 -} - -a.list-group-item-info { - color: #31708f -} - -a.list-group-item-info .list-group-item-heading { - color: inherit -} - -a.list-group-item-info:focus, a.list-group-item-info:hover { - color: #31708f; - background-color: #c4e3f3 -} - -a.list-group-item-info.active, a.list-group-item-info.active:focus, a.list-group-item-info.active:hover - { - color: #fff; - background-color: #31708f; - border-color: #31708f -} - -.list-group-item-warning { - color: #8a6d3b; - background-color: #fcf8e3 -} - -a.list-group-item-warning { - color: #8a6d3b -} - -a.list-group-item-warning .list-group-item-heading { - color: inherit -} - -a.list-group-item-warning:focus, a.list-group-item-warning:hover { - color: #8a6d3b; - background-color: #faf2cc -} - -a.list-group-item-warning.active, a.list-group-item-warning.active:focus, - a.list-group-item-warning.active:hover { - color: #fff; - background-color: #8a6d3b; - border-color: #8a6d3b -} - -.list-group-item-danger { - color: #a94442; - background-color: #f2dede -} - -a.list-group-item-danger { - color: #a94442 -} - -a.list-group-item-danger .list-group-item-heading { - color: inherit -} - -a.list-group-item-danger:focus, a.list-group-item-danger:hover { - color: #a94442; - background-color: #ebcccc -} - -a.list-group-item-danger.active, a.list-group-item-danger.active:focus, - a.list-group-item-danger.active:hover { - color: #fff; - background-color: #a94442; - border-color: #a94442 -} - -.list-group-item-heading { - margin-top: 0; - margin-bottom: 5px -} - -.list-group-item-text { - margin-bottom: 0; - line-height: 1.3 -} - -.panel { - margin-bottom: 20px; - background-color: #fff; - border: 1px solid transparent; - border-radius: 4px; - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: 0 1px 1px rgba(0, 0, 0, .05) -} - -.panel-body { - padding: 15px -} - -.panel-heading { - padding: 10px 15px; - border-bottom: 1px solid transparent; - border-top-left-radius: 3px; - border-top-right-radius: 3px -} - -.panel-heading>.dropdown .dropdown-toggle { - color: inherit -} - -.panel-title { - margin-top: 0; - margin-bottom: 0; - font-size: 16px; - color: inherit -} - -.panel-title>.small, .panel-title>.small>a, .panel-title>a, .panel-title>small, - .panel-title>small>a { - color: inherit -} - -.panel-footer { - padding: 10px 15px; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px -} - -.panel>.list-group, .panel>.panel-collapse>.list-group { - margin-bottom: 0 -} - -.panel>.list-group .list-group-item, .panel>.panel-collapse>.list-group .list-group-item - { - border-width: 1px 0; - border-radius: 0 -} - -.panel>.list-group:first-child .list-group-item:first-child, .panel>.panel-collapse>.list-group:first-child .list-group-item:first-child - { - border-top: 0; - border-top-left-radius: 3px; - border-top-right-radius: 3px -} - -.panel>.list-group:last-child .list-group-item:last-child, .panel>.panel-collapse>.list-group:last-child .list-group-item:last-child - { - border-bottom: 0; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px -} - -.panel-heading+.list-group .list-group-item:first-child { - border-top-width: 0 -} - -.list-group+.panel-footer { - border-top-width: 0 -} - -.panel>.panel-collapse>.table, .panel>.table, .panel>.table-responsive>.table - { - margin-bottom: 0 -} - -.panel>.panel-collapse>.table caption, .panel>.table caption, .panel>.table-responsive>.table caption - { - padding-right: 15px; - padding-left: 15px -} - -.panel>.table-responsive:first-child>.table:first-child, .panel>.table:first-child - { - border-top-left-radius: 3px; - border-top-right-radius: 3px -} - -.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child, - .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child, - .panel>.table:first-child>tbody:first-child>tr:first-child, .panel>.table:first-child>thead:first-child>tr:first-child - { - border-top-left-radius: 3px; - border-top-right-radius: 3px -} - -.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child, - .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child, - .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child, - .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child, - .panel>.table:first-child>tbody:first-child>tr:first-child td:first-child, - .panel>.table:first-child>tbody:first-child>tr:first-child th:first-child, - .panel>.table:first-child>thead:first-child>tr:first-child td:first-child, - .panel>.table:first-child>thead:first-child>tr:first-child th:first-child - { - border-top-left-radius: 3px -} - -.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child, - .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child, - .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child, - .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child, - .panel>.table:first-child>tbody:first-child>tr:first-child td:last-child, - .panel>.table:first-child>tbody:first-child>tr:first-child th:last-child, - .panel>.table:first-child>thead:first-child>tr:first-child td:last-child, - .panel>.table:first-child>thead:first-child>tr:first-child th:last-child - { - border-top-right-radius: 3px -} - -.panel>.table-responsive:last-child>.table:last-child, .panel>.table:last-child - { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px -} - -.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child, - .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child, - .panel>.table:last-child>tbody:last-child>tr:last-child, .panel>.table:last-child>tfoot:last-child>tr:last-child - { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px -} - -.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child, - .panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child, - .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child, - .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child, - .panel>.table:last-child>tbody:last-child>tr:last-child td:first-child, - .panel>.table:last-child>tbody:last-child>tr:last-child th:first-child, - .panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child, - .panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child - { - border-bottom-left-radius: 3px -} - -.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child, - .panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child, - .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child, - .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child, - .panel>.table:last-child>tbody:last-child>tr:last-child td:last-child, - .panel>.table:last-child>tbody:last-child>tr:last-child th:last-child, - .panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child, - .panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child { - border-bottom-right-radius: 3px -} - -.panel>.panel-body+.table, .panel>.panel-body+.table-responsive, .panel>.table+.panel-body, - .panel>.table-responsive+.panel-body { - border-top: 1px solid #ddd -} - -.panel>.table>tbody:first-child>tr:first-child td, .panel>.table>tbody:first-child>tr:first-child th - { - border-top: 0 -} - -.panel>.table-bordered, .panel>.table-responsive>.table-bordered { - border: 0 -} - -.panel>.table-bordered>tbody>tr>td:first-child, .panel>.table-bordered>tbody>tr>th:first-child, - .panel>.table-bordered>tfoot>tr>td:first-child, .panel>.table-bordered>tfoot>tr>th:first-child, - .panel>.table-bordered>thead>tr>td:first-child, .panel>.table-bordered>thead>tr>th:first-child, - .panel>.table-responsive>.table-bordered>tbody>tr>td:first-child, - .panel>.table-responsive>.table-bordered>tbody>tr>th:first-child, - .panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child, - .panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child, - .panel>.table-responsive>.table-bordered>thead>tr>td:first-child, - .panel>.table-responsive>.table-bordered>thead>tr>th:first-child { - border-left: 0 -} - -.panel>.table-bordered>tbody>tr>td:last-child, .panel>.table-bordered>tbody>tr>th:last-child, - .panel>.table-bordered>tfoot>tr>td:last-child, .panel>.table-bordered>tfoot>tr>th:last-child, - .panel>.table-bordered>thead>tr>td:last-child, .panel>.table-bordered>thead>tr>th:last-child, - .panel>.table-responsive>.table-bordered>tbody>tr>td:last-child, .panel>.table-responsive>.table-bordered>tbody>tr>th:last-child, - .panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child, .panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child, - .panel>.table-responsive>.table-bordered>thead>tr>td:last-child, .panel>.table-responsive>.table-bordered>thead>tr>th:last-child - { - border-right: 0 -} - -.panel>.table-bordered>tbody>tr:first-child>td, .panel>.table-bordered>tbody>tr:first-child>th, - .panel>.table-bordered>thead>tr:first-child>td, .panel>.table-bordered>thead>tr:first-child>th, - .panel>.table-responsive>.table-bordered>tbody>tr:first-child>td, - .panel>.table-responsive>.table-bordered>tbody>tr:first-child>th, - .panel>.table-responsive>.table-bordered>thead>tr:first-child>td, - .panel>.table-responsive>.table-bordered>thead>tr:first-child>th { - border-bottom: 0 -} - -.panel>.table-bordered>tbody>tr:last-child>td, .panel>.table-bordered>tbody>tr:last-child>th, - .panel>.table-bordered>tfoot>tr:last-child>td, .panel>.table-bordered>tfoot>tr:last-child>th, - .panel>.table-responsive>.table-bordered>tbody>tr:last-child>td, .panel>.table-responsive>.table-bordered>tbody>tr:last-child>th, - .panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td, .panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th - { - border-bottom: 0 -} - -.panel>.table-responsive { - margin-bottom: 0; - border: 0 -} - -.panel-group { - margin-bottom: 20px -} - -.panel-group .panel { - margin-bottom: 0; - border-radius: 4px -} - -.panel-group .panel+.panel { - margin-top: 5px -} - -.panel-group .panel-heading { - border-bottom: 0 -} - -.panel-group .panel-heading+.panel-collapse>.list-group, .panel-group .panel-heading+.panel-collapse>.panel-body - { - border-top: 1px solid #ddd -} - -.panel-group .panel-footer { - border-top: 0 -} - -.panel-group .panel-footer+.panel-collapse .panel-body { - border-bottom: 1px solid #ddd -} - -.panel-default { - border-color: #ddd -} - -.panel-default>.panel-heading { - color: #333; - background-color: #f5f5f5; - border-color: #ddd -} - -.panel-default>.panel-heading+.panel-collapse>.panel-body { - border-top-color: #ddd -} - -.panel-default>.panel-heading .badge { - color: #f5f5f5; - background-color: #333 -} - -.panel-default>.panel-footer+.panel-collapse>.panel-body { - border-bottom-color: #ddd -} - -.panel-primary { - border-color: #337ab7 -} - -.panel-primary>.panel-heading { - color: #fff; - background-color: #337ab7; - border-color: #337ab7 -} - -.panel-primary>.panel-heading+.panel-collapse>.panel-body { - border-top-color: #337ab7 -} - -.panel-primary>.panel-heading .badge { - color: #337ab7; - background-color: #fff -} - -.panel-primary>.panel-footer+.panel-collapse>.panel-body { - border-bottom-color: #337ab7 -} - -.panel-success { - border-color: #d6e9c6 -} - -.panel-success>.panel-heading { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6 -} - -.panel-success>.panel-heading+.panel-collapse>.panel-body { - border-top-color: #d6e9c6 -} - -.panel-success>.panel-heading .badge { - color: #dff0d8; - background-color: #3c763d -} - -.panel-success>.panel-footer+.panel-collapse>.panel-body { - border-bottom-color: #d6e9c6 -} - -.panel-info { - border-color: #bce8f1 -} - -.panel-info>.panel-heading { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1 -} - -.panel-info>.panel-heading+.panel-collapse>.panel-body { - border-top-color: #bce8f1 -} - -.panel-info>.panel-heading .badge { - color: #d9edf7; - background-color: #31708f -} - -.panel-info>.panel-footer+.panel-collapse>.panel-body { - border-bottom-color: #bce8f1 -} - -.panel-warning { - border-color: #faebcc -} - -.panel-warning>.panel-heading { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc -} - -.panel-warning>.panel-heading+.panel-collapse>.panel-body { - border-top-color: #faebcc -} - -.panel-warning>.panel-heading .badge { - color: #fcf8e3; - background-color: #8a6d3b -} - -.panel-warning>.panel-footer+.panel-collapse>.panel-body { - border-bottom-color: #faebcc -} - -.panel-danger { - border-color: #ebccd1 -} - -.panel-danger>.panel-heading { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1 -} - -.panel-danger>.panel-heading+.panel-collapse>.panel-body { - border-top-color: #ebccd1 -} - -.panel-danger>.panel-heading .badge { - color: #f2dede; - background-color: #a94442 -} - -.panel-danger>.panel-footer+.panel-collapse>.panel-body { - border-bottom-color: #ebccd1 -} - -.embed-responsive { - position: relative; - display: block; - height: 0; - padding: 0; - overflow: hidden -} - -.embed-responsive .embed-responsive-item, .embed-responsive embed, - .embed-responsive iframe, .embed-responsive object, .embed-responsive video - { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: 0 -} - -.embed-responsive-16by9 { - padding-bottom: 56.25% -} - -.embed-responsive-4by3 { - padding-bottom: 75% -} - -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05) -} - -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, .15) -} - -.well-lg { - padding: 24px; - border-radius: 6px -} - -.well-sm { - padding: 9px; - border-radius: 3px -} - -.close { - float: right; - font-size: 21px; - font-weight: 700; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - filter: alpha(opacity = 20); - opacity: .2 -} - -.close:focus, .close:hover { - color: #000; - text-decoration: none; - cursor: pointer; - filter: alpha(opacity = 50); - opacity: .5 -} - -button.close { - -webkit-appearance: none; - padding: 0; - cursor: pointer; - background: 0 0; - border: 0 -} - -.modal-open { - overflow: hidden -} - -.modal { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1050; - display: none; - overflow: hidden; - -webkit-overflow-scrolling: touch; - outline: 0 -} - -.modal.fade .modal-dialog { - -webkit-transition: -webkit-transform .3s ease-out; - -o-transition: -o-transform .3s ease-out; - transition: transform .3s ease-out; - -webkit-transform: translate(0, -25%); - -ms-transform: translate(0, -25%); - -o-transform: translate(0, -25%); - transform: translate(0, -25%) -} - -.modal.in .modal-dialog { - -webkit-transform: translate(0, 0); - -ms-transform: translate(0, 0); - -o-transform: translate(0, 0); - transform: translate(0, 0) -} - -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto -} - -.modal-dialog { - position: relative; - width: auto; - margin: 10px -} - -.modal-content { - position: relative; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - outline: 0; - -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); - box-shadow: 0 3px 9px rgba(0, 0, 0, .5) -} - -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000 -} - -.modal-backdrop.fade { - filter: alpha(opacity = 0); - opacity: 0 -} - -.modal-backdrop.in { - filter: alpha(opacity = 50); - opacity: .5 -} - -.modal-header { - min-height: 16.43px; - padding: 15px; - border-bottom: 1px solid #e5e5e5 -} - -.modal-header .close { - margin-top: -2px -} - -.modal-title { - margin: 0; - line-height: 1.42857143 -} - -.modal-body { - position: relative; - padding: 15px -} - -.modal-footer { - padding: 15px; - text-align: right; - border-top: 1px solid #e5e5e5 -} - -.modal-footer .btn+.btn { - margin-bottom: 0; - margin-left: 5px -} - -.modal-footer .btn-group .btn+.btn { - margin-left: -1px -} - -.modal-footer .btn-block+.btn-block { - margin-left: 0 -} - -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll -} - -@media ( min-width :768px) { - .modal-dialog { - width: 600px; - margin: 30px auto - } - .modal-content { - -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - box-shadow: 0 5px 15px rgba(0, 0, 0, .5) - } - .modal-sm { - width: 300px - } -} - -@media ( min-width :992px) { - .modal-lg { - width: 900px - } -} - -.tooltip { - position: absolute; - z-index: 1070; - display: block; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 12px; - font-weight: 400; - line-height: 1.4; - filter: alpha(opacity = 0); - opacity: 0 -} - -.tooltip.in { - filter: alpha(opacity = 90); - opacity: .9 -} - -.tooltip.top { - padding: 5px 0; - margin-top: -3px -} - -.tooltip.right { - padding: 0 5px; - margin-left: 3px -} - -.tooltip.bottom { - padding: 5px 0; - margin-top: 3px -} - -.tooltip.left { - padding: 0 5px; - margin-left: -3px -} - -.tooltip-inner { - max-width: 200px; - padding: 3px 8px; - color: #fff; - text-align: center; - text-decoration: none; - background-color: #000; - border-radius: 4px -} - -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid -} - -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-width: 5px 5px 0; - border-top-color: #000 -} - -.tooltip.top-left .tooltip-arrow { - right: 5px; - bottom: 0; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000 -} - -.tooltip.top-right .tooltip-arrow { - bottom: 0; - left: 5px; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000 -} - -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-width: 5px 5px 5px 0; - border-right-color: #000 -} - -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-width: 5px 0 5px 5px; - border-left-color: #000 -} - -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000 -} - -.tooltip.bottom-left .tooltip-arrow { - top: 0; - right: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000 -} - -.tooltip.bottom-right .tooltip-arrow { - top: 0; - left: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000 -} - -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1060; - display: none; - max-width: 276px; - padding: 1px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - font-weight: 400; - line-height: 1.42857143; - text-align: left; - white-space: normal; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - box-shadow: 0 5px 10px rgba(0, 0, 0, .2) -} - -.popover.top { - margin-top: -10px -} - -.popover.right { - margin-left: 10px -} - -.popover.bottom { - margin-top: 10px -} - -.popover.left { - margin-left: -10px -} - -.popover-title { - padding: 8px 14px; - margin: 0; - font-size: 14px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-radius: 5px 5px 0 0 -} - -.popover-content { - padding: 9px 14px -} - -.popover>.arrow, .popover>.arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid -} - -.popover>.arrow { - border-width: 11px -} - -.popover>.arrow:after { - content: ""; - border-width: 10px -} - -.popover.top>.arrow { - bottom: -11px; - left: 50%; - margin-left: -11px; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, .25); - border-bottom-width: 0 -} - -.popover.top>.arrow:after { - bottom: 1px; - margin-left: -10px; - content: " "; - border-top-color: #fff; - border-bottom-width: 0 -} - -.popover.right>.arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, .25); - border-left-width: 0 -} - -.popover.right>.arrow:after { - bottom: -10px; - left: 1px; - content: " "; - border-right-color: #fff; - border-left-width: 0 -} - -.popover.bottom>.arrow { - top: -11px; - left: 50%; - margin-left: -11px; - border-top-width: 0; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, .25) -} - -.popover.bottom>.arrow:after { - top: 1px; - margin-left: -10px; - content: " "; - border-top-width: 0; - border-bottom-color: #fff -} - -.popover.left>.arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-right-width: 0; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, .25) -} - -.popover.left>.arrow:after { - right: 1px; - bottom: -10px; - content: " "; - border-right-width: 0; - border-left-color: #fff -} - -.carousel { - position: relative -} - -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden -} - -.carousel-inner>.item { - position: relative; - display: none; - -webkit-transition: .6s ease-in-out left; - -o-transition: .6s ease-in-out left; - transition: .6s ease-in-out left -} - -.carousel-inner>.item>a>img, .carousel-inner>.item>img { - line-height: 1 -} - -@media all and (transform-3d) , ( -webkit-transform-3d ) { - .carousel-inner>.item { - -webkit-transition: -webkit-transform .6s ease-in-out; - -o-transition: -o-transform .6s ease-in-out; - transition: transform .6s ease-in-out; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-perspective: 1000; - perspective: 1000 - } - .carousel-inner>.item.active.right, .carousel-inner>.item.next { - left: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0) - } - .carousel-inner>.item.active.left, .carousel-inner>.item.prev { - left: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0) - } - .carousel-inner>.item.active, .carousel-inner>.item.next.left, - .carousel-inner>.item.prev.right { - left: 0; - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0) - } -} - -.carousel-inner>.active, .carousel-inner>.next, .carousel-inner>.prev { - display: block -} - -.carousel-inner>.active { - left: 0 -} - -.carousel-inner>.next, .carousel-inner>.prev { - position: absolute; - top: 0; - width: 100% -} - -.carousel-inner>.next { - left: 100% -} - -.carousel-inner>.prev { - left: -100% -} - -.carousel-inner>.next.left, .carousel-inner>.prev.right { - left: 0 -} - -.carousel-inner>.active.left { - left: -100% -} - -.carousel-inner>.active.right { - left: 100% -} - -.carousel-control { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 15%; - font-size: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); - filter: alpha(opacity = 50); - opacity: .5 -} - -.carousel-control.left { - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0, - rgba(0, 0, 0, .0001) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0, - rgba(0, 0, 0, .0001) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), - to(rgba(0, 0, 0, .0001))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0, - rgba(0, 0, 0, .0001) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', - endColorstr='#00000000', GradientType=1); - background-repeat: repeat-x -} - -.carousel-control.right { - right: 0; - left: auto; - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0, - rgba(0, 0, 0, .5) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0, - rgba(0, 0, 0, .5) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), - to(rgba(0, 0, 0, .5))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0, - rgba(0, 0, 0, .5) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', - endColorstr='#80000000', GradientType=1); - background-repeat: repeat-x -} - -.carousel-control:focus, .carousel-control:hover { - color: #fff; - text-decoration: none; - filter: alpha(opacity = 90); - outline: 0; - opacity: .9 -} - -.carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-next, .carousel-control .icon-prev { - position: absolute; - top: 50%; - z-index: 5; - display: inline-block -} - -.carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev - { - left: 50%; - margin-left: -10px -} - -.carousel-control .glyphicon-chevron-right, .carousel-control .icon-next - { - right: 50%; - margin-right: -10px -} - -.carousel-control .icon-next, .carousel-control .icon-prev { - width: 20px; - height: 20px; - margin-top: -10px; - font-family: serif; - line-height: 1 -} - -.carousel-control .icon-prev:before { - content: '\2039' -} - -.carousel-control .icon-next:before { - content: '\203a' -} - -.carousel-indicators { - position: absolute; - bottom: 10px; - left: 50%; - z-index: 15; - width: 60%; - padding-left: 0; - margin-left: -30%; - text-align: center; - list-style: none -} - -.carousel-indicators li { - display: inline-block; - width: 10px; - height: 10px; - margin: 1px; - text-indent: -999px; - cursor: pointer; - background-color: #000 \9; - background-color: rgba(0, 0, 0, 0); - border: 1px solid #fff; - border-radius: 10px -} - -.carousel-indicators .active { - width: 12px; - height: 12px; - margin: 0; - background-color: #fff -} - -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6) -} - -.carousel-caption .btn { - text-shadow: none -} - -@media screen and (min-width:768px) { - .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-next, .carousel-control .icon-prev { - width: 30px; - height: 30px; - margin-top: -15px; - font-size: 30px - } - .carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev - { - margin-left: -15px - } - .carousel-control .glyphicon-chevron-right, .carousel-control .icon-next - { - margin-right: -15px - } - .carousel-caption { - right: 20%; - left: 20%; - padding-bottom: 30px - } - .carousel-indicators { - bottom: 20px - } -} - -.btn-group-vertical>.btn-group:after, .btn-group-vertical>.btn-group:before, - .btn-toolbar:after, .btn-toolbar:before, .clearfix:after, .clearfix:before, - .container-fluid:after, .container-fluid:before, .container:after, - .container:before, .dl-horizontal dd:after, .dl-horizontal dd:before, - .form-horizontal .form-group:after, .form-horizontal .form-group:before, - .modal-footer:after, .modal-footer:before, .nav:after, .nav:before, - .navbar-collapse:after, .navbar-collapse:before, .navbar-header:after, - .navbar-header:before, .navbar:after, .navbar:before, .pager:after, - .pager:before, .panel-body:after, .panel-body:before, .row:after, .row:before - { - display: table; - content: " " -} - -.btn-group-vertical>.btn-group:after, .btn-toolbar:after, .clearfix:after, - .container-fluid:after, .container:after, .dl-horizontal dd:after, - .form-horizontal .form-group:after, .modal-footer:after, .nav:after, - .navbar-collapse:after, .navbar-header:after, .navbar:after, .pager:after, - .panel-body:after, .row:after { - clear: both -} - -.center-block { - display: block; - margin-right: auto; - margin-left: auto -} - -.pull-right { - float: right !important -} - -.pull-left { - float: left !important -} - -.hide { - display: none !important -} - -.show { - display: block !important -} - -.invisible { - visibility: hidden -} - -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0 -} - -.hidden { - display: none !important -} - -.affix { - position: fixed -} - -@ --ms-viewport { - width: device-width -} - -.visible-lg, .visible-md, .visible-sm, .visible-xs { - display: none !important -} - -.visible-lg-block, .visible-lg-inline, .visible-lg-inline-block, - .visible-md-block, .visible-md-inline, .visible-md-inline-block, - .visible-sm-block, .visible-sm-inline, .visible-sm-inline-block, - .visible-xs-block, .visible-xs-inline, .visible-xs-inline-block { - display: none !important -} - -@media ( max-width :767px) { - .visible-xs { - display: block !important - } - table.visible-xs { - display: table - } - tr.visible-xs { - display: table-row !important - } - td.visible-xs, th.visible-xs { - display: table-cell !important - } -} - -@media ( max-width :767px) { - .visible-xs-block { - display: block !important - } -} - -@media ( max-width :767px) { - .visible-xs-inline { - display: inline !important - } -} - -@media ( max-width :767px) { - .visible-xs-inline-block { - display: inline-block !important - } -} - -@media ( min-width :768px)and (max-width:991px) { - .visible-sm { - display: block !important - } - table.visible-sm { - display: table - } - tr.visible-sm { - display: table-row !important - } - td.visible-sm, th.visible-sm { - display: table-cell !important - } -} - -@media ( min-width :768px)and (max-width:991px) { - .visible-sm-block { - display: block !important - } -} - -@media ( min-width :768px)and (max-width:991px) { - .visible-sm-inline { - display: inline !important - } -} - -@media ( min-width :768px)and (max-width:991px) { - .visible-sm-inline-block { - display: inline-block !important - } -} - -@media ( min-width :992px)and (max-width:1199px) { - .visible-md { - display: block !important - } - table.visible-md { - display: table - } - tr.visible-md { - display: table-row !important - } - td.visible-md, th.visible-md { - display: table-cell !important - } -} - -@media ( min-width :992px)and (max-width:1199px) { - .visible-md-block { - display: block !important - } -} - -@media ( min-width :992px)and (max-width:1199px) { - .visible-md-inline { - display: inline !important - } -} - -@media ( min-width :992px)and (max-width:1199px) { - .visible-md-inline-block { - display: inline-block !important - } -} - -@media ( min-width :1200px) { - .visible-lg { - display: block !important - } - table.visible-lg { - display: table - } - tr.visible-lg { - display: table-row !important - } - td.visible-lg, th.visible-lg { - display: table-cell !important - } -} - -@media ( min-width :1200px) { - .visible-lg-block { - display: block !important - } -} - -@media ( min-width :1200px) { - .visible-lg-inline { - display: inline !important - } -} - -@media ( min-width :1200px) { - .visible-lg-inline-block { - display: inline-block !important - } -} - -@media ( max-width :767px) { - .hidden-xs { - display: none !important - } -} - -@media ( min-width :768px)and (max-width:991px) { - .hidden-sm { - display: none !important - } -} - -@media ( min-width :992px)and (max-width:1199px) { - .hidden-md { - display: none !important - } -} - -@media ( min-width :1200px) { - .hidden-lg { - display: none !important - } -} - -.visible-print { - display: none !important -} - -@media print { - .visible-print { - display: block !important - } - table.visible-print { - display: table - } - tr.visible-print { - display: table-row !important - } - td.visible-print, th.visible-print { - display: table-cell !important - } -} - -.visible-print-block { - display: none !important -} - -@media print { - .visible-print-block { - display: block !important - } -} - -.visible-print-inline { - display: none !important -} - -@media print { - .visible-print-inline { - display: inline !important - } -} - -.visible-print-inline-block { - display: none !important -} - -@media print { - .visible-print-inline-block { - display: inline-block !important - } -} - -@media print { - .hidden-print { - display: none !important - } -} \ No newline at end of file diff --git a/_examples/file-server/embedding-files-into-app/assets/favicon.ico b/_examples/file-server/embedding-files-into-app/assets/favicon.ico deleted file mode 100644 index c370da518e..0000000000 Binary files a/_examples/file-server/embedding-files-into-app/assets/favicon.ico and /dev/null differ diff --git a/_examples/file-server/embedding-files-into-app/assets/js/jquery-2.1.1.js b/_examples/file-server/embedding-files-into-app/assets/js/jquery-2.1.1.js deleted file mode 100644 index 07f9351406..0000000000 --- a/_examples/file-server/embedding-files-into-app/assets/js/jquery-2.1.1.js +++ /dev/null @@ -1 +0,0 @@ -console.log("example"); \ No newline at end of file diff --git a/_examples/file-server/embedding-files-into-app/bindata.go b/_examples/file-server/embedding-files-into-app/bindata.go deleted file mode 100644 index ea37b16536..0000000000 --- a/_examples/file-server/embedding-files-into-app/bindata.go +++ /dev/null @@ -1,382 +0,0 @@ -// Code generated by go-bindata. (@generated) DO NOT EDIT. - -//Package main generated by go-bindata.// sources: -// assets/css/bootstrap.min.css -// assets/favicon.ico -// assets/js/jquery-2.1.1.js -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data []byte, name string) ([]byte, error) { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -// Name return file name -func (fi bindataFileInfo) Name() string { - return fi.name -} - -// Size return file size -func (fi bindataFileInfo) Size() int64 { - return fi.size -} - -// Mode return file mode -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} - -// ModTime return file modify time -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} - -// IsDir return file whether a directory -func (fi bindataFileInfo) IsDir() bool { - return fi.mode&os.ModeDir != 0 -} - -// Sys return file is sys mode -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -type assetFile struct { - *bytes.Reader - name string - childInfos []os.FileInfo - childInfoOffset int -} - -type assetOperator struct{} - -// Open implement http.FileSystem interface -func (f *assetOperator) Open(name string) (http.File, error) { - var err error - if len(name) > 0 && name[0] == '/' { - name = name[1:] - } - content, err := Asset(name) - if err == nil { - return &assetFile{name: name, Reader: bytes.NewReader(content)}, nil - } - children, err := AssetDir(name) - if err == nil { - childInfos := make([]os.FileInfo, 0, len(children)) - for _, child := range children { - childPath := filepath.Join(name, child) - info, errInfo := AssetInfo(filepath.Join(name, child)) - if errInfo == nil { - childInfos = append(childInfos, info) - } else { - childInfos = append(childInfos, newDirFileInfo(childPath)) - } - } - return &assetFile{name: name, childInfos: childInfos}, nil - } else { - // If the error is not found, return an error that will - // result in a 404 error. Otherwise the server returns - // a 500 error for files not found. - if strings.Contains(err.Error(), "not found") { - return nil, os.ErrNotExist - } - return nil, err - } -} - -// Close no need do anything -func (f *assetFile) Close() error { - return nil -} - -// Readdir read dir's children file info -func (f *assetFile) Readdir(count int) ([]os.FileInfo, error) { - if len(f.childInfos) == 0 { - return nil, os.ErrNotExist - } - if count <= 0 { - return f.childInfos, nil - } - if f.childInfoOffset+count > len(f.childInfos) { - count = len(f.childInfos) - f.childInfoOffset - } - offset := f.childInfoOffset - f.childInfoOffset += count - return f.childInfos[offset : offset+count], nil -} - -// Stat read file info from asset item -func (f *assetFile) Stat() (os.FileInfo, error) { - if len(f.childInfos) != 0 { - return newDirFileInfo(f.name), nil - } - return AssetInfo(f.name) -} - -// newDirFileInfo return default dir file info -func newDirFileInfo(name string) os.FileInfo { - return &bindataFileInfo{ - name: name, - size: 0, - mode: os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir - modTime: time.Time{}} -} - -// AssetFile return a http.FileSystem instance that data backend by asset -func AssetFile() http.FileSystem { - return &assetOperator{} -} - -var _cssBootstrapMinCss = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xbd\xef\x8f\xe3\x38\x92\x20\xfa\xb9\x1a\xe8\xff\x41\x5b\x8d\x41\x57\x4d\x59\x2e\xf9\x77\xda\x89\xce\xb7\xfb\xe6\x0e\xb7\x03\xdc\xec\x97\x9b\x0f\x07\xf4\xf4\x7b\xa0\x25\xda\xd6\x94\x2c\x69\x24\x39\x2b\xb3\xfb\xf6\xfe\xf6\x83\xf8\x4b\x41\x32\x48\xd1\x4e\x77\xcf\xdc\x62\xb7\xee\x7a\x9c\x62\x44\x30\x18\x0c\x06\x83\x41\x32\xf8\xf9\xf7\xff\xf4\xed\x37\xd1\xef\xa3\xff\xb7\xaa\xba\xb6\x6b\x48\x1d\x3d\x2f\xa6\x8b\xe9\x32\xfa\x70\xea\xba\x7a\xf7\xf9\xf3\x91\x76\x7b\x59\x36\x4d\xab\xf3\x47\x06\xfe\x87\xaa\x7e\x6d\xf2\xe3\xa9\x8b\xe6\xc9\x6c\x16\xcf\x93\xd9\x2a\xfa\xf3\xd7\xbc\xeb\x68\x33\x89\xfe\x58\xa6\x53\x06\xf5\xdf\xf3\x94\x96\x2d\xcd\xa2\x4b\x99\xd1\x26\xfa\xd3\x1f\xff\xcc\xc9\xb6\x3d\xdd\xbc\x3b\x5d\xf6\x3d\xc5\xcf\xdd\xd7\x7d\xfb\x59\x55\xf2\x79\x5f\x54\xfb\xcf\x67\xd2\x76\xb4\xf9\xfc\xdf\xff\xf8\x87\xff\xfa\x6f\xff\xe3\xbf\xb2\x4a\x3f\x47\x9f\x7f\xff\x4f\x51\x59\x35\x67\x52\xe4\x3f\xd3\x69\xda\xb6\x3d\xb3\xc9\x74\x1e\xfd\x2f\x46\x5b\x54\x17\xfd\xaf\xe8\x98\x77\xd3\xbc\xfa\xac\x60\xa3\xdf\x7f\xfe\xf6\x9b\x53\x77\x2e\xa2\x5f\xbe\xfd\xe6\xdd\xa1\x2a\xbb\xf8\x40\xce\x79\xf1\xba\x8b\x5a\x52\xb6\x71\x4b\x9b\xfc\xf0\xf8\xed\x37\xef\xe2\xaf\x74\xff\x25\xef\xe2\x8e\xbe\x74\x71\x9b\xff\x4c\x63\x92\xfd\xf5\xd2\x76\xbb\x68\x96\x24\xbf\x63\x10\xe7\xd6\x51\xfa\xed\x37\xff\xfe\xed\x37\xdf\x7e\xb3\xaf\xb2\x57\x56\xcd\x99\x34\xc7\xbc\xdc\x45\x89\x28\x20\x4d\x97\xa7\x05\x9d\x44\xa4\xcd\x33\x3a\x89\x32\xda\x91\xbc\x68\x27\xd1\x21\x3f\xa6\xa4\xee\xf2\xaa\x64\xbf\x2f\x0d\x9d\x44\x87\xaa\x62\xb2\x3c\x51\x92\xb1\xff\x3d\x36\xd5\xa5\x9e\x30\xb2\x79\x39\x89\xce\xb4\xbc\x4c\xa2\x92\x3c\x4f\xa2\x96\xa6\x1c\xb7\xbd\x9c\xcf\xa4\xe1\x95\x67\x79\x5b\x17\xe4\x75\x17\xed\x8b\x2a\xfd\x22\x39\xb8\x64\x79\x35\x89\x52\x52\x3e\x93\x76\x12\xd5\x4d\x75\x6c\x68\xdb\x4e\xa2\xe7\x3c\xa3\x95\x8e\x97\x97\x45\x5e\xd2\x98\xa1\xf7\xed\x7e\xa6\x3d\xfb\xa4\x88\x49\x91\x1f\xcb\x5d\xb4\x27\x2d\xed\x21\x20\xe9\x5d\x59\x75\xd1\x87\x1f\xd3\xaa\xec\x9a\xaa\x68\x7f\x8a\x3e\x6a\x24\xcb\xaa\xa4\x3d\xa9\x13\xed\x35\x67\x10\xcc\x8f\xa7\x3c\xcb\x68\xf9\xd3\x24\xea\xe8\xb9\x2e\x48\x47\x23\x0b\x4f\x56\xc3\x4a\xf6\x24\xfd\xd2\xcb\xa3\xcc\xe2\xb4\x2a\xaa\x66\x17\x75\x0d\x29\xdb\x9a\x34\xb4\xec\x24\xe4\x8e\xa4\x5d\xfe\xdc\x8b\x7b\x77\xaa\x9e\x69\xc3\x30\xab\x4b\xd7\x33\x0d\x3a\x65\xbf\x6f\x7e\xec\xf2\xae\xa0\x3f\x71\xd2\x55\x93\xd1\x26\xde\x57\x5d\x57\x9d\x77\xd1\xac\x7e\x89\xb2\xaa\xeb\x68\x26\x7b\x77\x12\xb5\x5d\x53\x95\xc7\x41\x93\xbe\x8a\xe6\x6c\x12\x49\x34\x3b\x94\x43\x71\xdb\xbd\x16\x74\x17\xe5\x1d\x29\xf2\x54\x00\x9c\x66\x9a\x86\x4c\xd7\x1b\x7a\x8e\x92\x47\x85\x92\xff\x4c\x77\xd1\x9c\x9e\x05\xf8\x99\x34\x5f\x18\x82\x68\xed\x77\x49\xc2\x80\x07\x39\xec\xa2\xef\x0e\x07\x59\x7d\x7b\x26\x05\xd0\x74\x4e\xed\x41\x29\x68\x7b\xe9\x1b\x71\xa9\x19\x44\x5d\xb5\x79\xaf\x3d\xbb\xa8\xa1\x05\xe9\x05\x66\x70\xb1\x59\x31\xb5\x67\xca\xa0\x3a\x2e\x40\x21\x64\x05\x5d\x55\xef\xa2\x78\xba\x52\x8d\x69\x2f\x7b\x21\x69\x2e\xe2\x78\x3a\x1f\x0a\xf3\xf3\x11\x74\xc3\xd0\x4d\xed\xf3\x91\x2b\xd7\xae\xa9\xaa\x8e\xeb\x55\xdf\xa9\x87\xa2\xfa\xba\x8b\xb8\xfe\x08\x50\x3e\x82\x34\xf9\xce\xe8\x39\x5a\x26\xf5\x8b\x94\x3e\xd7\x05\xad\x35\x72\xe0\xef\xab\x97\xbe\xe1\x79\x79\xdc\x45\xbd\x1e\xd3\x92\x7d\xe3\x23\xbf\xfa\xd9\x57\xee\x28\x12\x95\xd6\x82\xa7\x81\x6b\x72\xe9\x2a\x51\x98\x56\xbd\x41\xf8\xb2\xcf\xfa\x41\x49\x27\x51\x4b\xce\xb5\x6d\xaa\xce\x55\x59\xb5\x35\x49\xe9\x64\xf8\x69\xf4\xd6\x4c\x49\x72\x7f\xe9\xba\xde\x28\xe4\x65\x7d\xe9\x26\x51\x55\x77\xdc\x82\x44\x2d\x2d\x68\xda\xf5\x63\xed\xa5\x23\x0d\x25\xba\xad\x92\xf4\x7a\x03\x70\xa2\x4d\xde\x3d\x0e\x6a\x27\xbe\x68\x15\x18\x6d\x7a\xce\xdb\x7c\x5f\x50\x83\x07\x5e\x25\x57\x87\xde\x74\xb2\xd1\x7a\xa8\x9a\xb3\x36\xb6\x25\x34\xb3\xd3\x8c\xed\x1f\xbb\xd7\x9a\xfe\xc0\xbf\xff\x34\x81\xdf\x1a\xda\xd2\x4e\xff\xd4\x5e\xf6\xe7\xbc\xe3\xa3\x58\xf6\x26\xa9\x6b\x4a\x1a\x52\xa6\x74\x17\x71\x32\xac\x39\x97\xa6\xed\xdb\x53\x57\x79\xd9\xd1\x46\xab\xfe\xc7\x2c\x6f\xc9\xbe\xa0\xd9\x4f\x1a\x23\xea\x2b\x1f\x86\x82\x40\x46\x0f\xe4\x52\xe8\x02\xd9\xed\x98\x9e\x1c\xaa\xf4\xd2\xc6\x79\x59\xf6\xc6\x9b\xd1\xb0\x0b\xf8\x00\x24\x59\xc6\x54\x86\x8f\x68\x43\xef\x19\x26\x83\xd3\x06\x20\x9f\xd8\x20\x0c\x97\x41\x7a\xa2\xe9\x97\x7d\xf5\x62\x08\x8b\x64\x79\xa5\x0b\x06\xea\xaa\x32\x79\xb8\x96\xeb\xc5\xee\x92\xa1\x21\x36\x5f\xe5\xe5\xbc\xa7\xcd\x4f\xbb\x9d\xac\x9f\xb5\x3f\x6e\xeb\xbc\x8c\x35\x45\x75\x80\x57\x97\x4e\x07\xff\xf6\x9b\x77\x70\x08\x83\xa1\x04\x35\x82\x92\x26\x3d\xb9\x1b\x7e\x9f\xf1\xfd\xe8\xd0\xb7\x5e\xd3\x0f\x39\x2d\x32\x27\x63\x43\xfb\xf8\x87\x38\xed\x31\x0b\x4c\x22\x2e\x8c\x8c\xa6\x55\x43\x7a\x03\x2e\x24\x82\x71\x02\xc6\x18\x63\xa8\xa5\x9d\xae\x7a\xd3\xc5\x8a\x9e\xa3\xe9\x7a\xce\xfe\x67\xb3\xa2\xe7\x47\x68\x13\xa2\x79\xfd\x02\x95\xb3\x9f\x14\xdb\xaa\xc8\xb3\xa8\xcd\x8b\x67\x35\x80\x0a\x7a\xa4\x65\x16\xa0\xd4\x9a\xe5\x41\xed\xa1\xb4\x56\xbe\x49\xb6\xeb\x07\x24\x9c\xb3\x7b\x7b\x68\x56\xda\xfb\x07\x05\xa9\x5b\xda\x77\x19\xff\x25\xd1\xb3\x49\xd4\x9d\x0c\x6e\x59\xd9\xbb\xde\xcd\xfc\x1f\xd5\xa5\xe9\x65\x87\xb8\xab\xa7\xd5\xbe\xfe\xdc\xdb\x86\x55\xbc\xaf\xf2\x82\x36\xcc\x65\xd1\xdc\xd6\xb6\x49\x3f\xa7\x6d\xfb\xb9\xf7\xd5\x98\x9f\xda\xfb\x9f\xff\x7c\xa6\x59\x4e\xa2\xba\xc9\x4b\x2e\xff\xdf\x4f\xa2\x1d\x39\x30\x37\x6f\xb7\xa7\x87\x4a\xcc\x10\x70\x96\x8f\xfe\x29\x3f\xd7\x55\xd3\x91\x92\x19\x62\x6e\x3e\xdb\x13\xc9\x7a\x81\xf5\xfd\x6a\x02\x40\x97\x20\x89\x2c\x7c\x6d\x18\xf8\xc8\xb8\xcb\xbf\xfd\xe6\x5d\x2f\x24\xd2\x3b\x56\xbd\xb9\xef\x28\xef\x73\xce\xdb\xa0\x90\x3b\xee\xf5\x73\x97\x80\xa3\xfc\x78\x6a\xe8\xe1\x27\xde\x66\xd9\x54\x36\x8e\x76\xd1\xfb\xe8\xc3\xfb\x88\x74\x5d\xf3\xa1\x87\xf9\x18\xbd\xff\xf8\x5e\x62\x0d\x1e\xda\x08\x26\x03\xd2\x50\x59\x85\xff\xdf\x0f\xef\xff\x4a\x9e\x49\x9b\x36\x79\xdd\xed\xde\xff\x24\x65\xae\x4a\xbf\x7b\xef\xa0\x2c\xe9\x30\x27\xf8\x6f\x97\xaa\xa3\x6c\x7e\xe6\x60\xf6\x68\xf8\x6e\xbb\xdd\x32\xe9\xd5\xe4\x48\xe3\x7d\x43\xc9\x97\x38\x2f\x7b\x67\x7f\x17\x91\xe7\x2a\xcf\x04\xb9\xae\x77\xea\x39\x11\xe5\xe3\x32\x6d\x8e\xb9\xb7\x1f\x33\xdd\x17\xc0\xf9\xf9\x38\x89\x3a\xc1\xda\x08\x61\xe9\x3d\xbd\x3b\x93\x97\xf8\x6b\x9e\x75\x27\xbe\x32\xb1\x7b\xef\x34\x9f\x44\xa7\xc5\x24\xe2\x23\xec\x5d\xd5\xd4\x27\x52\xb6\xbb\x68\xc1\xf8\xff\x9a\x67\xd5\xd7\xfe\x2f\x0d\xda\x62\x81\xc9\x4c\xe7\x00\xcc\xf4\xa6\x77\x7a\xb0\xb9\x98\x96\xe4\x79\x4f\x1a\x43\x14\xdc\x5c\x71\x80\x7d\x57\x3e\x4d\x53\xd2\xd0\x6e\x12\x4d\xb3\xa6\xaa\x2f\xf5\x13\xf8\x08\x7b\x22\xee\xaa\x3a\xc6\x87\x8e\xa4\x56\x90\x3d\x2d\x9c\xbd\x97\xf4\xa6\x85\x03\x0e\xb6\xc5\x6d\x47\x10\xfa\x1c\xad\xb7\x2c\xf2\xe7\xc9\x14\x85\xe2\x10\x17\x08\x57\x03\x5e\x27\xcd\x00\x29\xf0\xed\xe4\x6c\x41\x96\x65\x16\x4d\x66\xec\xfe\x59\xf8\x91\x29\xb5\xbd\xca\xef\xff\x5b\xf1\x5a\x9f\xf2\xb4\x2a\xdb\xe8\x5f\x49\x71\x28\xf2\xf2\xd8\x7e\xdf\xeb\x41\xdb\xa4\xbb\xe8\xd2\x14\x1f\xa6\xd3\xcf\x3d\x4a\xfb\xf9\xa8\x40\xe3\x93\x04\x8d\x1b\x7a\xbc\x14\xa4\x99\xd2\xaa\xfb\x78\x1b\xda\xff\xf3\x5d\x4e\x0f\xf9\xcb\xc7\xbe\x59\xbd\x5b\x48\xba\x0f\xdf\xd3\xf3\x9e\x66\x19\xcd\xe2\xaa\xa6\x65\x3f\x07\x7e\xff\xb1\x5f\xfd\xbe\x0b\x27\xfc\xb5\x3a\x1c\xe6\x1f\x23\x49\x90\xfd\x79\x13\x11\x9d\xc6\xd5\x24\xba\x0e\x50\xe8\x9a\x0b\xbd\xa9\x35\xed\xf3\xf1\xbb\x01\xe0\xff\x57\x00\xa2\x5c\x93\x5d\xfb\x7c\xfc\xfe\xa3\xe8\xfa\xa9\x42\xf2\xac\xf7\xd8\x22\x6d\xc6\x67\x79\x67\x04\x20\x50\x6b\xe0\xa2\x97\xfb\xa9\x8f\xe6\x24\xbe\xe4\xcb\x57\xcd\xa5\x9d\x41\x3f\x8a\xd3\x38\x57\x55\x77\x62\x13\x33\x29\xbb\x9c\x14\x39\x69\x69\xa6\x3c\xb5\xaa\x7d\xb1\xe0\x8e\x0d\x79\x6d\x53\xa2\x16\x20\x43\xe3\x63\x36\x31\xe7\xed\x17\x38\xd3\x0e\x96\xfe\x2f\x73\xf2\xde\xc6\xa9\x8b\x4b\xeb\x82\xdf\x23\xf0\xf4\xd2\x08\xf0\x49\xa4\x7f\xae\x5c\x64\x12\x92\x22\x84\xce\x79\xe9\xae\x79\x3e\x9b\x23\x28\x69\x51\x5d\x32\x17\xca\x3a\x99\x61\xec\x96\xcf\xb4\xa8\x6a\xea\xc2\xda\x24\x5b\x4c\x28\xb4\x4c\xf3\xc2\x8d\x73\x40\x70\x8e\x05\x69\x5d\xed\xa1\x09\xca\xdc\xf9\xd2\xe6\xa9\x1b\x05\x13\x01\xf7\x89\xdd\x38\x0b\x04\xe7\x44\x49\xd3\xb9\x51\x56\x58\x35\x1d\x69\xdc\x18\x6b\x07\x46\x4c\xcf\x75\xf7\xea\xc6\xdb\x20\x78\x97\x96\x7a\x6a\x7a\x40\x30\x0e\x79\x71\x76\x63\x60\xdd\xd9\x9d\xe2\x82\x34\x47\x97\x12\xd0\x64\x96\xa0\x58\x6e\x78\xac\x37\xfb\x5a\xf2\xd6\x2d\x68\x54\xa5\x2b\xd7\x60\xa5\xc9\x0c\xeb\xcb\x86\x9e\xab\x67\x4f\x43\x96\x08\xce\xcf\x55\x75\x8e\xf3\xd2\x8d\x84\x69\x00\x43\xaa\x2e\x9e\xe6\x60\x5a\x50\x1d\x0e\x6e\x04\xac\xfb\xdb\xfc\x58\x12\xd7\x48\xa3\xc9\x0c\x53\x80\xb4\x3a\xba\x11\xd0\xfe\x6f\x48\xeb\xee\xcc\x39\xd6\xf9\xa7\xea\xec\x96\xf2\x1c\xeb\xfe\x43\x5e\x78\x30\xb0\xbe\xef\x72\x5f\x1d\x68\xef\x57\xc4\x65\xff\x68\x32\xc7\xfa\x3e\xab\xbe\x96\x45\x45\xb2\x98\x14\xee\xae\x9c\x63\x0a\x20\x31\xdd\x58\x98\x02\x5c\x6a\x3f\x0e\xa6\x03\x79\xb9\xaf\x5e\xdc\x28\x98\x0a\xf4\xb3\x77\x9c\xe6\x4d\xea\x93\x39\xa6\x0a\x0d\xad\x29\x71\x4b\x62\x81\xe9\x42\x43\x0f\x0d\xf5\x28\xd0\x02\x53\x87\xde\x14\x78\x85\xbe\xc0\x54\xa2\xf7\x43\xdc\x18\x98\x4a\x1c\x0a\xe2\x1e\x0d\x0b\x4c\x25\xfa\x05\x58\x7d\xaa\x4a\xea\x9e\xad\x16\x98\x42\x3c\x57\xc5\xe5\x4c\xbd\x43\x7c\x81\xa9\x84\xc0\xeb\xf5\xc9\x8d\x88\xe9\x85\x40\xbc\xd4\x6e\x34\x4c\x37\xfe\xd6\xa4\x55\xe6\x56\x8b\x05\xa6\x16\x7b\xe2\x47\x5a\xa2\x13\x84\x47\xf2\x4b\x74\x86\x20\x47\xb7\xcc\x97\x98\x3e\xec\x2b\xcf\x04\xb1\xc4\xf4\xa1\xc7\x38\x93\xc6\x83\x85\xe9\x04\x0b\xd8\xb8\x51\x30\x75\x48\xc9\x99\x36\xc4\x8d\x83\xa9\x02\x8b\xba\x3b\x31\x30\x1d\xd8\x57\x85\xdb\x9a\x2c\xb1\xee\xe7\x9b\x50\x6e\x1c\x74\x82\xa0\x2f\x9d\xf4\xd2\x5d\x88\x2b\x54\x05\x7a\x44\x1e\x85\x70\xe2\x61\x9a\xc0\xf6\x93\xe2\x82\x1e\x3c\xf5\x61\xfa\xc0\xf1\x52\x5a\x76\x1e\xaf\x69\x85\xe9\x05\xc7\x6c\xfc\x4d\xc4\x54\x83\x23\xfe\xf5\xd2\x76\xf9\xc1\xed\xdb\xad\x30\x15\xf1\xba\x43\x2b\x4c\x41\xf2\x32\xa3\x65\x37\x22\x18\x7c\x0e\x61\x88\x23\xed\x43\xdd\x49\x92\xd2\x7e\x26\x8e\xd9\x06\xb1\x1b\x17\x5d\x27\xe4\x69\x77\x69\xdc\x66\x63\x8d\xe9\xcc\x99\xd4\x71\x3f\x42\x3d\x3d\xb8\x46\xfb\x9e\xef\xc3\x3b\x71\xb0\x5e\xef\x7c\xc3\x7a\x8d\x75\x37\xcd\x72\x0f\x06\xba\x56\x38\x11\x9f\x08\xb0\x6e\x66\x7b\x38\x6e\x14\xac\x83\xbd\x6e\xef\x1a\xeb\xd8\xb6\xa3\x75\xbc\x27\xe9\x97\xaf\xa4\x71\xdb\x90\x35\xd6\xaf\x07\xd2\x76\xe3\xa8\x1b\xac\x77\xc7\xb1\x30\x7b\xc0\xa2\x11\x4e\x0c\x4c\x1b\x6a\x72\x69\xdd\x02\xd9\x60\xca\xd0\x76\x95\x7b\x2a\xdd\x60\xca\x70\xa8\x1a\x7f\x5b\x30\x7d\x60\xc2\x1b\xc5\xc4\xd7\x90\xb4\x1e\xc7\xc4\xb4\x83\xfe\x95\xa6\x6e\xb5\xdd\xa0\xab\x88\x13\x7d\x6e\xaa\x11\x23\xbc\xc1\xb4\x43\x62\xfa\x8d\xcd\x03\xa6\x1d\x75\x71\x69\xd9\x9a\xc7\x8d\x86\x06\x0a\xf2\x72\x14\x0f\x53\x12\xbe\x5a\x1c\x41\xc4\x54\xa5\xfa\x32\x82\x84\x69\xcb\xdf\x2e\xb4\xed\x72\xb1\xa8\x73\xa3\x62\x3a\x93\x97\x87\x6a\x04\x0d\x55\x98\xb4\xa1\xb4\x6c\x4f\x95\xa7\x1b\x30\x75\x11\x72\x19\x59\x40\x3c\x60\x6a\x53\x7d\x19\x45\xc3\x1d\xcc\x72\x0c\x6f\x8b\x29\x0c\x69\x9a\xea\xab\x5f\x47\xb7\xa8\x83\xc1\xf0\xfc\x1a\xba\x45\x67\x19\x86\xe8\xf1\xb9\xb7\xa8\x77\xc1\xb0\xbc\x2e\xfe\x16\x53\x19\x36\x77\x78\x97\x49\x5b\x4c\x5d\x1a\xca\x4e\xa6\x1d\x2e\x85\x3b\x74\xb0\xc5\x14\x46\x20\xb2\xd3\x43\x6e\x4c\xd4\xc2\xbc\xa4\x05\x39\x93\x51\xfd\x9e\xa1\x91\xbe\x63\xee\xee\xc0\x19\x1a\xe8\x2b\x28\x71\xae\xb3\x66\x68\x98\xef\x90\xbb\xa7\xe1\x59\x82\xce\xf5\xaf\x94\x6d\x3d\xb8\xb1\x30\xe1\xf7\x58\x69\x51\xb9\x67\x9f\x19\x1a\x20\xfc\x4a\x9a\x32\x2f\x8f\x23\xc2\xc3\x44\x5f\x17\xa4\xf4\x54\x86\x1a\x77\x52\xd0\x32\x73\xc7\x30\x67\x68\x9c\xb0\x21\x65\x56\x39\x63\x8b\x33\x34\x4a\x98\x56\xe7\x33\x75\x3b\x59\x33\x34\x54\x78\x26\xc7\x92\x7a\x70\xd0\xe0\xb7\x98\x75\xdc\x43\x73\x86\x46\x0c\x25\x9e\x6f\x70\xce\xd0\xb8\x61\x43\xbb\xaf\xd4\xc7\x26\xee\x0d\x56\x75\xdd\xf7\x73\xea\x09\x3a\xcf\xd0\xe0\xe1\xa1\x2a\xd8\x36\xa4\x57\xb7\xd0\x28\xa2\xc0\xf4\xea\x32\x1a\x4a\x14\xf6\x40\x9e\xf3\x73\x23\xe3\xb1\x24\x86\x7c\xaa\x9a\xfc\xe7\xaa\xec\x3c\xe8\x78\x88\x31\x73\x3a\x39\x33\x34\xc2\xb8\xbf\x14\xc5\xa9\x6a\xdc\x4d\x44\xa3\x8c\x7b\xea\x36\x75\x33\x34\xca\x98\xf6\xd2\x38\xe4\x29\xe9\xdc\xdd\x80\x06\x1b\xbb\xd3\xe5\xbc\x6f\x7d\x1a\x8a\x46\x1a\x05\x9a\x57\x41\xd1\x60\xe3\x89\x94\x99\x7f\x8e\x9b\xa1\x01\x47\x86\xe7\x9b\x53\x67\x68\xd0\x91\xa1\xf9\x1a\x87\x29\x09\x43\xf2\x36\x0d\x8d\x39\x72\x5f\x21\x64\x1a\x9f\xa1\xe1\x47\x0d\xdf\xdb\x54\x34\x0e\xa9\xa1\x7b\x9a\x8c\x86\x24\x35\x64\x7f\xd3\x31\x2d\x3a\x16\xd5\xde\xad\x78\x68\x68\xf2\x6b\x43\x4b\xf7\xae\xd8\x0c\x0d\x4b\x76\xa4\xfd\xe2\x8c\xc6\xcd\xd0\x80\xe4\x21\x2f\x3c\x71\x97\x19\x1a\x8d\xdc\x37\x39\x3d\xa4\xc4\x63\xd1\xd0\x80\x64\xef\xda\x70\xef\xd6\x89\x87\xc6\x24\x33\xd2\x9e\xf6\x95\x67\xfd\x34\x43\x23\x93\x35\xa9\x69\x93\x16\xb9\xbb\xa7\xd1\xf0\x24\xdb\x59\xf4\xef\xfa\xcd\xd0\x28\x65\x91\x97\xce\xf5\xff\x0c\x8f\x50\x9e\x2a\x8f\x13\x80\x46\x28\xeb\x4b\x7b\xaa\xdd\xfb\x5e\x33\x34\x44\x79\x69\x3d\xa2\xc3\x3a\xf8\xb8\xf7\x08\x0d\xeb\xda\xb6\xf2\x4c\x8c\x68\x94\xb1\xc7\x88\xf7\xaf\x31\x29\xea\x13\xd9\x7b\x66\x64\x34\xd6\x68\x62\xfb\xdc\xed\x19\x1a\x75\x94\x14\xf8\x69\x1c\x27\x2a\x1a\x73\x80\xa8\xfe\x9a\xd1\xf5\x81\xe4\xbd\xeb\x9a\x7c\x7f\xe9\xdc\x7b\x16\x33\x34\x02\x69\xe3\xfb\x79\x40\x35\xa2\x64\xe1\x2a\xea\xd6\x0b\x34\x22\x49\x5f\x6a\x52\x7a\x70\xf0\x9d\x4d\x7e\xee\xca\x6f\x35\xd1\x50\xa4\x42\xf5\x58\x6b\x34\x1c\x59\x54\x47\xcf\xe6\xf0\x6c\x8d\xee\x75\x16\x9e\x0d\xd5\x19\x1a\xbd\xec\xab\xf1\x6c\x27\xcf\xd0\xf0\x65\x49\xbf\xc6\x5f\xf3\x32\xab\xbe\xba\xf1\x70\xcf\x35\xad\x3c\x26\x10\x0f\x63\x12\x77\x80\x71\x86\x46\x31\xbd\xee\x26\x1a\xc4\xec\xeb\xf0\xb0\x85\x6e\x67\xb0\xa3\x6e\x6e\x1c\x4c\x17\xe8\x8b\x17\x07\x8d\x5b\xb6\xd4\xa3\xac\x68\xcc\xf2\x50\x54\x75\xfd\x1a\x67\xee\x03\x47\x74\x86\x86\x2e\x05\xa2\x5f\x18\x68\x04\x53\x60\xfa\x0f\x41\xcc\xf0\x50\xe6\x50\xa9\x1b\x11\x0d\x67\x72\x44\x6f\x67\xa3\xd1\xcc\xb4\xa1\x59\xde\xf5\xeb\x20\x4f\x2b\x31\x2d\xe1\x57\x47\x3c\x96\x16\x8f\x67\x5e\xba\x82\x36\xee\x79\x18\x0d\x65\xf2\xc3\xb8\x4e\x1c\x34\x86\x99\x56\xe7\xba\xa1\x6d\xeb\xe9\x3c\x34\x88\x49\x49\xe3\x9f\xc4\xd1\x10\x26\x43\xf1\x1a\x6d\x34\x80\xd9\x55\x5f\x7d\xed\x42\xe7\x9a\x8e\x74\xee\xe9\x05\x0d\x5b\xb6\x99\x7f\xd7\x68\x86\x46\x2d\x4f\xa3\x58\xa8\xed\xb8\xec\xd9\xe1\x6f\x0f\x8b\xe8\x2e\x08\x3b\x91\xdb\x76\xb4\xf1\x55\x88\xfb\x29\x17\xb6\x74\x29\xf6\x6e\xa5\x42\x63\x96\x1c\x71\x15\xcf\xdc\x68\xb8\x9f\xd2\xa3\xad\x7d\x68\xb8\x73\xd2\xa3\x6d\x7c\x68\xe8\x22\x45\xde\xee\x8d\x7d\xbb\xe5\x33\x34\x6a\xd9\xd0\x63\xde\x76\xfc\x0a\xc0\x08\x3a\xba\x73\x5e\x54\x97\x6c\xf4\x7c\xcd\x0c\x0d\x43\x72\x5c\xff\x29\x9b\xd9\x16\x53\x84\xae\xa1\x34\x4e\xab\x32\xf7\x59\x96\x2d\x7e\x7c\x8a\xd2\x38\xa3\x69\x9e\x5d\x2a\xe7\x91\x4d\x3a\x4f\x50\x63\xe1\xe4\x72\x8e\x06\x4a\x7b\xfb\xec\x3d\x4a\x35\x47\xa3\xa5\xbd\x75\x1e\x41\x43\x97\x21\xf4\x99\x16\x1e\x8f\x69\x8e\x86\x4d\x7b\xd5\x71\x63\xa0\x2b\x11\xd2\xba\x63\x29\x73\x34\x5c\x4a\x0a\xea\x9e\xc2\xe7\x68\xf8\x92\xfe\xed\xc2\x6e\x82\x3b\xbb\x77\x8e\x46\x30\xbf\xe4\xa5\xf3\x1c\xcb\x1c\x0d\x5f\xfe\xed\xe2\x59\x97\xe2\x47\x77\x6b\xe2\x76\x68\xe7\x68\xdc\x72\x9f\xb7\x27\xf7\x7e\xe5\x1c\x8d\x58\x7e\x29\x7d\x91\x92\x39\x1a\xb0\xdc\x93\xfd\x6b\x7c\xa8\x9a\xf3\xa5\x70\x9e\x66\x99\xa3\xf1\xca\xce\x1d\xf7\x9d\xaf\x0f\xd8\x61\xeb\x7d\x41\xd2\x2f\xde\xe5\xf9\x1c\x0d\x53\xee\xdd\x73\xed\x1c\x0d\x4d\x92\xba\x76\x8e\x85\xc3\xc3\x01\x3b\xbf\x4c\x1b\x4f\x90\x62\x8e\x06\x24\x4f\xd5\xa5\xf1\x1d\x7b\x9e\x2f\x66\xd8\x11\xf2\x82\x9c\xdd\xfd\x8a\x46\x24\xb3\x4b\x5d\x78\xe3\x91\x73\x34\x1e\x59\xe7\xc7\xe3\x6b\xbc\x27\xee\x58\xc3\x1c\x0d\x48\xb6\x69\xde\xb6\x55\xe3\x36\x75\x68\x34\x72\x9f\x77\x69\xe5\x5e\x49\xcd\xd1\x50\xe4\xbe\x73\x1e\x55\xc2\x11\x5e\xf6\x6e\xfd\x46\x11\x5e\x9d\x43\x35\x49\x08\xd6\xfa\xbf\x3a\xad\x9b\x03\xa1\xb9\xec\x9d\xca\x36\x4f\xf6\x19\x8e\x72\x1d\x02\xbb\xf1\xe0\x6c\x38\x1a\x42\xcd\x53\x1a\x17\x55\x51\xb8\x6d\x35\x1a\x39\x55\x68\x71\xd7\x5b\x6d\xf7\xc0\x43\x03\xa7\x34\xbb\xa4\xfc\x6a\xa0\x13\x0d\xdd\x6f\x67\xa9\x31\x02\xb6\x12\xe6\x68\xc8\x54\xa0\x8f\x6d\x63\xcc\xd1\xe0\xe9\x99\x96\x97\xf8\x44\xce\xfb\x4b\x73\xf4\xcc\x1d\x68\x10\xf5\x5c\x65\xa4\x18\x59\xa2\xcf\xd1\x58\x6a\xe5\xbc\x5f\x41\xe7\x68\x20\xf5\xd8\x10\xcf\xe0\x42\x83\xa8\xed\xa5\x64\xe6\xc9\xed\x33\xcf\xf1\x83\x9d\x32\xf7\x89\x1b\x0d\x3d\xde\xd9\xa3\xf1\xbb\x6f\x4e\x3c\xf4\x1c\x78\x8f\x07\x6e\x12\x3a\x91\x51\xcd\xd9\xff\x95\xa6\x9d\x38\xa5\xe7\x39\xe0\x33\x47\xa3\xaa\x1a\xb6\xc8\x56\xe1\x24\x80\x29\x8f\x46\x20\x40\x7d\xd1\x98\xab\x46\xc4\xb7\x59\x31\x47\xcf\x88\x6a\xe8\xa3\x63\x00\x0d\xe2\x6a\x24\xbc\xdb\x2d\x73\xfc\x00\x69\x93\x93\xf2\x58\xd0\x11\x5c\xfc\x0c\xa9\xc4\xf5\xb6\x1c\x0d\xed\x2a\xd4\x91\xae\x43\xa3\xba\x0a\xd9\xa7\x35\x68\x50\x37\xad\xca\xb6\xf2\x98\x63\x3c\x94\x7b\xa9\x69\x23\x2e\x28\x3b\x11\xd1\xd9\xf8\xb2\x1f\x43\x43\x4d\x53\x6f\xd6\xfc\x22\x45\xcf\x19\xf6\x68\x23\xbd\x88\x69\x10\xc3\xf3\x85\x6d\xe7\x68\xd8\x96\xa1\x79\x16\x20\x43\xc8\xf6\xf7\xac\xf0\x57\x4a\x6e\x21\xea\xc0\xae\xea\xff\xba\x35\xea\x09\xab\x44\x82\x97\xa4\xd6\x32\x4e\x74\xa4\x8e\x4f\xf9\xf1\x54\xb0\xe5\xba\xb8\x5c\xdc\x1c\xf7\xe4\x43\x32\x89\xc4\xff\x93\x57\x41\x55\x66\x2a\xed\x26\xe7\xfb\x7f\xa5\xc5\x33\xed\xed\x42\xf4\x6f\xf4\x42\xdf\x4f\x22\xf5\x61\x12\xfd\x4b\x93\x93\x62\x62\x24\xc9\x82\xec\x2c\x39\x3b\xfa\x55\xce\xe9\x72\xfe\xb0\xda\xcc\x96\x0b\x90\x3c\xe6\xbb\xc5\x62\xf1\x88\xe6\x6e\xfa\xee\x70\x38\x48\x0e\xf5\xa4\x35\x68\xaa\x1a\x8d\x79\x90\xa4\x06\x70\x05\xbe\x6a\x8c\xe9\x09\x6c\x88\xd0\x28\xc9\xde\x86\xec\x37\x8f\x32\x45\x0d\xcc\x63\x00\xf3\x4f\xed\x58\xfe\x16\x3d\xa9\x94\x24\x31\x5f\xac\xe6\x9b\x14\x25\x01\x52\x21\x40\x3a\x0c\x5d\xe5\xa4\xea\x4e\x79\x29\xb2\x4d\x3d\xc2\xef\xab\xfa\x85\x25\xc7\x88\x86\xeb\xb1\xe9\xa5\x8d\x1b\x76\x92\xa4\xaf\x1b\x40\xc7\xd5\xe1\xd0\xd2\x6e\x17\xc5\x73\x95\xef\x08\xc9\x88\xa4\x72\xb4\x88\x8c\x01\x66\x32\xa7\x73\x9e\x65\xc3\x2d\xda\x94\x34\xd5\xa5\xa5\x05\x4f\xdb\xf2\x34\xcd\x3b\x7a\x7e\x22\x4f\x2c\x35\x01\x5e\xc8\x8b\xf2\xf3\x31\x6e\x68\x5b\x57\x65\x9b\x3f\xd3\x09\xbb\xe0\x7e\xba\x9c\xf7\x25\xc9\x8b\x48\xe2\xab\x2f\x4f\x92\x19\x3d\x77\x19\x4f\x45\xa2\xe5\x33\x78\xc4\x53\xbf\xf0\xfa\x7a\xd5\x12\x29\x29\xc4\x88\x6a\x48\x96\x5f\xda\x5d\xb4\x56\x12\x61\x90\x03\x2b\xbf\xf8\xae\x3d\x8f\xd4\xad\xa5\xbe\x19\x1f\x0d\xb8\xfa\xe3\xd9\x55\xbe\xcb\xb2\xec\xd1\x6e\xc6\xd2\xb0\x00\x0d\x29\xe5\x9d\x6e\x52\x14\xd1\x74\xde\x46\x94\xb4\x34\xce\xcb\xb8\xba\xb0\x41\x10\x57\x21\x50\x23\x20\x50\x74\xfc\x14\x03\x26\xe3\x95\x4a\x33\x26\xb2\x6c\x71\x8d\x63\xf3\x68\x34\x17\xc6\x4b\x7c\x93\x19\xc0\xe4\x67\x95\x27\x06\x34\x5a\xde\x4c\x97\x22\xa1\x54\x69\x65\xdb\xc4\x55\x59\x70\x8b\x36\x5c\x6b\x27\xfb\xb6\x2a\x2e\x1d\xbb\xd6\x2e\xbb\x8d\x93\x57\x1d\x52\x1b\xf9\x8a\x60\xb2\x9b\x58\x94\x9a\xc9\xc5\x98\x25\x2b\xf2\x7a\x17\x35\x34\xed\xa0\x71\xc5\x32\xdc\x48\xde\xf8\x48\x25\xfd\x12\x50\x66\xa3\x43\x8a\x06\x53\x30\x34\xa3\xed\x48\x97\xa7\xa0\x11\x52\xd7\x4c\xdd\xd3\x32\x77\x59\x89\xb8\x06\xb6\xc1\x38\xf9\xb1\xa9\x0a\x95\x56\x8b\x5b\x30\x34\x23\xd6\xf4\x34\x9b\x44\xd3\xd3\xbc\xff\xcf\xa2\xff\xcf\xb2\xff\xcf\xaa\xff\xcf\x7a\x12\xf5\x85\x32\x8d\x48\x5f\xd2\x17\x9c\xd6\xe3\x26\x5a\x26\x01\x58\x61\x49\x00\xa6\x33\x67\xbe\xb1\xe9\x69\x16\x4d\xd9\xe1\xd4\x9e\x81\x59\xa4\x7e\xce\xc1\xe7\xf9\xf0\x79\x01\x3e\x2f\x86\xcf\x4b\xf9\xb9\xb7\x46\xa7\xe5\x50\xb0\x02\xf0\xab\xe1\xf3\x1a\x7c\x5e\xcb\xcf\x80\x15\xc5\x09\xcb\x93\x32\x7c\x56\x9c\x00\x46\x06\x3e\x06\x36\xa2\x81\x87\x81\x85\x9e\x96\xe2\x01\xb0\x20\x39\x18\xa4\x3c\x9a\x52\x41\x5a\x99\xcd\x66\x83\x77\xeb\xd0\x8f\xa1\xe3\x75\x36\xa4\xd2\xbb\x4b\xa7\x0c\x34\xfa\x76\x2b\x22\xa1\xd2\x34\x5d\xa4\xf5\xea\x77\x8a\x3b\x5d\x63\x75\x2d\x85\x2d\x9d\x05\xb4\x74\x09\x78\xbf\x55\x6f\xa0\xf6\x61\x1d\x1f\x05\x76\xbb\xca\xcd\x08\xfb\x54\x64\x95\x04\x00\x0b\x30\xe5\xb1\x3e\x9e\x5b\x10\xb0\x85\x0b\xa5\x05\x30\x0d\xe5\x12\xca\x80\xb5\xc1\xf4\x49\x1f\x00\xc4\x8a\xb5\xc1\x84\x80\x34\xd6\xba\x9d\x10\x10\x83\xbb\x52\xeb\x9e\x4a\x94\x68\xdd\x50\xc8\xdc\x49\x8e\x49\x04\xd2\x5c\x83\x4f\x72\xa0\x2c\x50\xb3\xb3\x14\xe4\x45\x8e\xae\x0f\xd1\x39\x2f\xf9\xac\x1f\xed\x36\xeb\x87\xfa\xe5\x23\xab\x73\xa8\x5d\x93\xd0\xac\x67\x4f\x25\xdb\x91\xbd\x86\xa7\xe1\x1c\xba\xec\x4c\x9a\x2f\x93\x48\xe5\xf6\x1c\xb2\xb1\xcd\x79\xfe\x35\xcc\x55\x48\x0f\x0f\x74\x21\x09\x30\x2f\xb3\x5f\xc5\x31\x7c\xf6\x97\x70\xdf\xfa\x8f\x1a\x14\xcf\xd5\x6b\x82\xb1\xaf\x1a\x1c\xbf\x3d\x69\x01\xf2\xcf\x1a\xa4\xb8\xf4\x68\x81\x8a\xef\x1a\x6c\x59\x7d\x6d\x08\xef\xd6\xaf\xa7\xbc\xa3\x2c\x55\x1b\x4b\x0f\xd3\x7f\xd7\x9b\x53\x7d\xa5\x4d\x4a\x5a\x3a\x10\x06\xd9\x22\x55\xa9\x86\x73\xa9\x6b\x0f\x8e\x2a\xd5\x1b\x4a\x6a\x76\x19\xf6\x67\x1c\x69\x28\xd6\xb0\xce\x17\x99\xed\x0c\x31\xab\x0c\xa2\x6e\x72\x95\x83\x57\x5f\x5a\x48\xcf\x5f\x83\xc3\x56\x11\x0f\xeb\x64\x9b\x68\x44\xdb\x4b\x9a\xd2\xb6\xd5\x89\xa6\x9b\xf5\x22\xd3\x89\x0a\x38\x8c\xe8\x7e\xb5\x9c\xa7\x1a\xd1\xbc\x3c\x54\x3a\xc5\xd9\x26\x79\x38\xe8\x14\x7b\x20\x8c\xdc\x72\x35\x5f\x6f\x35\x72\xe2\x0a\x83\x06\xf6\x40\xd6\xd9\x62\xaf\x53\x14\x70\x08\xd1\xf5\x7a\x35\x33\x78\xcc\x48\x79\x34\xa0\xc8\x76\xb9\x5c\xce\x75\x9a\x1c\x0c\x21\xf9\xb0\x5c\xac\x16\x72\x6c\x4f\xf7\x47\xb4\x7b\xa4\xff\x6d\x0f\x37\xa3\xe3\x06\x7c\x50\x15\x82\xa6\xf7\xe0\xfe\xa8\xf5\x1f\x02\x9f\x1d\x0e\x49\xf6\x00\xab\xb1\x3b\x12\x41\x4b\x67\x74\xbe\x5f\x80\x6a\x54\x8f\x62\x75\x6c\x69\x76\xd0\x9a\x62\x74\x2d\x82\x43\x0e\xd9\x76\x70\xb7\xf7\x47\xad\x8f\xc7\xac\x13\x01\x08\xfe\x6a\x0e\x1b\x9a\xee\x57\xa0\x1a\xd0\xeb\x18\xf8\x3c\xa3\x19\x85\xb5\x58\xdd\x8f\x60\xd1\xe5\x7e\xbb\x57\x1a\xcb\x92\xd8\xf1\xf3\x3d\xd0\xf6\xaa\xc9\x64\x0b\xbd\x81\x1d\xcb\x1d\x1c\x25\xc6\x3a\x45\xcb\x11\x6d\xad\x4e\xaa\x62\x12\x5d\x0a\xcb\xcf\x48\xfc\x4e\x46\x55\x44\x3d\x62\x55\x44\x17\x8e\x2f\xc8\xe8\x94\x24\xa2\x52\x31\x96\x4f\xe3\x52\xb2\xa4\x5b\x5a\x02\x4e\x1e\xe3\x8b\xc4\x8c\xd7\x82\xbc\x5c\x2a\x12\xc1\x91\xf9\xa2\xd7\x85\x2a\xea\xe5\x5f\xe2\x95\x5c\xe4\x8e\xd2\x7b\x2a\x72\xff\xda\x5a\xd6\xd5\x88\x25\x81\xb6\x32\x13\xf5\xad\x94\x74\xb2\x30\x79\xce\x07\x79\x66\xd9\x24\xca\x90\xfc\xb9\xc3\x9a\x5c\x02\x76\xb6\x4b\x0d\xf2\x79\x6b\x1e\x87\x10\x4c\xa0\xc7\x90\x15\x20\xf4\x2f\x99\x79\x77\x28\x2a\xd2\xf1\x79\x5a\x66\x5c\x64\x2b\xd5\xb5\x50\x31\x74\xfd\xf9\x2e\x2d\x28\x69\x00\x96\x35\x97\x0f\x5f\x07\x7c\x5a\x14\x79\xdd\xe6\x2d\xaf\x07\x9b\x7e\x79\xea\x41\x83\x51\xe1\xe6\x68\x6d\x9e\x3d\x24\x9a\xa7\xc3\x52\x73\x66\xa4\x23\x71\xd5\xe4\xc7\xbc\x24\x45\xcc\x13\x75\x4e\x22\x33\xaf\xba\x5c\x61\x9e\x68\x51\x3b\xc6\x10\x8f\x7c\x69\x53\x6a\x5e\xe6\x2c\xf1\x5b\x7b\x36\xfd\xa8\x2d\x8f\xc4\x8c\x4d\xf6\x43\xe6\x4e\xdd\xc7\xea\xc7\x9c\xb1\xbc\xe1\xae\x26\xe6\x46\x6e\xa6\x2b\x6d\xe0\x2b\xbd\xb4\x87\x3d\xa8\xaf\x2a\x76\x05\x69\xbb\x38\x3d\xe5\x45\x36\x89\x40\x49\xed\x2a\xb8\x40\x14\x91\xd0\xd7\x31\xe6\x01\x96\xf4\x37\xc1\x27\xf9\x7a\x00\xf8\x34\x78\xa3\x76\x78\x4d\x4f\x13\x1f\x18\xcf\x1d\xba\xc9\xe2\x45\x84\xc8\x11\x96\xb0\x12\x88\x22\x1a\xad\xc2\xfc\xdf\xff\x65\x9e\xcc\x96\xd1\x5f\x92\xe4\x5f\x92\xef\xd5\x14\xa1\x70\xe3\x86\x3e\xd3\xa6\xd5\xe8\x4d\xeb\x4b\x51\x00\x87\xd7\xb0\x31\x33\xd4\xc8\x24\x8f\x98\x6b\x0c\x83\x6f\xca\x42\x81\x4e\xb7\x74\x22\x71\xb3\x68\x8a\x06\x03\xd1\x65\xc4\xf2\x9f\xda\x40\x2e\x09\xc3\x76\xeb\x75\x69\x19\x6c\x21\x98\xb3\x4f\x20\x90\xb7\x7b\x3c\x7d\x22\x99\x10\xdb\x26\x9e\xf6\x72\x08\x6f\x73\x05\x11\x6f\x6b\x15\x19\x6f\x63\xbd\x94\x00\xa1\xc8\xd0\xc3\x5e\x03\x23\xa6\x8d\xb2\xcd\x24\xcb\x1a\xe9\xd5\x79\x17\xa3\x66\x2e\x4c\xff\x54\x14\xf6\x16\xc0\x9f\x68\x59\x54\x93\xe8\x4f\x55\x49\xd2\x6a\x12\xfd\x81\xed\x3a\x92\x76\x12\xbd\xff\x43\x75\x69\x72\xda\x44\xff\x46\xbf\xbe\x07\x0f\x05\x00\xea\xba\x29\x9c\xd7\x2f\x32\xa4\x6c\xdb\x57\xe5\x6b\x6e\xe6\xab\x25\x75\xad\x4a\xb7\x87\xf9\x61\x89\x47\xaa\x45\xb5\x5f\xf6\xd9\x0d\xb5\xfa\x3c\xf3\x05\x52\xdf\x42\x8f\x8c\xc3\x24\xd6\x79\xd9\xd2\x2e\x4a\x58\x7c\x37\x4a\x8c\x1d\xb2\xe9\x7c\xf5\x51\xed\xc6\x85\x22\x80\x96\x59\xad\x33\x9f\xf2\x90\x1b\x07\xa6\x7f\xe1\xe2\x56\xbe\x94\x62\x7e\x93\x11\x12\xb1\x9b\x63\x5b\x72\xc5\xc1\x56\xce\x59\x66\x1c\xc5\xe4\x6c\x71\xed\x06\xde\xd7\xaa\xc9\x78\x02\xe8\x5d\x24\xf2\x40\x17\x85\x2a\xe8\x3d\x0a\xf9\xbd\xff\xe0\x52\x99\x55\xff\xcf\xb1\xed\x91\xa6\xa9\x57\x99\xfa\xe6\xdb\x7a\x6c\xca\xdc\xf9\x7e\xc5\xa3\x19\x86\xa8\x1b\xca\xf8\xc6\x79\x05\x4f\xcb\x20\x5c\x29\x8b\xdf\x13\x69\xd3\xa6\x2a\x0a\x95\x3b\xfa\x4c\x5e\x94\x48\x17\xcb\x44\xdf\x58\x88\x5f\x77\x11\x87\x57\xbb\x6c\xbd\xe7\x95\x1b\xef\x42\xf8\xa7\xad\x99\xd6\xc9\x12\x56\xdf\x1a\x10\xa0\x20\xfe\x3f\xe6\xb2\xea\x8c\x48\xdf\x74\xb3\xd2\x9d\x3f\x8c\xca\x76\x3b\x1f\xa1\xb2\xdd\x8c\x53\x99\xcd\x93\x64\x84\xcc\x6c\x66\xd0\x19\xe0\xe2\x43\x71\xc9\xb3\x5f\x5b\x86\xd3\xa6\xfa\x0a\x2d\xbf\x40\x8b\x0d\x6a\x62\xc9\x34\x1b\x16\x31\xd3\xb4\x2a\xe2\xe2\x18\xcf\x26\x91\xfa\x99\x80\xdf\xf0\xfb\x7c\xf8\x0d\x7e\x2e\x26\x5c\x2e\xec\x8f\xe5\xf0\x7d\x35\xfc\x5c\x0f\x3f\x37\xc3\xcf\x87\xe1\xe7\x56\xd1\x38\x67\x8a\x95\xfe\x67\x02\x7e\xc3\xef\xf3\xe1\x37\xf8\xb9\x80\x64\x96\xc3\xf7\xd5\xf0\x73\x3d\xfc\xdc\x0c\x3f\x1f\x86\x9f\x03\x2b\xed\x59\xb1\xd2\xff\x4c\xc0\x6f\xf8\x7d\x3e\xfc\x06\x3f\x17\x90\xcc\x72\xf8\xbe\x1a\x7e\xae\x87\x9f\x9b\xe1\xe7\xc3\xf0\x73\x60\xe5\xa5\x55\xac\xf4\x3f\x13\xf0\x1b\x7e\x9f\x0f\xbf\xc1\xcf\x05\x24\xb3\x1c\xbe\xaf\x86\x9f\xeb\xe1\xe7\x66\xf8\xf9\x30\xfc\xdc\x1a\xfb\x81\x30\x5b\x77\x3f\x54\xf0\xcd\xcc\x71\x4d\x87\x5a\xf8\x0f\xd2\x48\xb0\x16\x36\xb9\xe3\xdb\x15\x60\xf7\xdd\x04\x98\x41\x80\xed\x6c\xba\xe6\xff\xb7\xb1\x00\x13\x08\xf8\xb0\x98\x2e\xc4\xff\x99\x80\x5b\x08\x07\xf6\x57\x24\xf3\xb0\x78\xbd\x76\xd6\xb7\x81\x70\xab\x07\x67\x75\x6b\x0d\xce\x6a\xdf\x0a\x16\x2f\xdd\xcd\x5b\x42\xb8\x85\xbb\x75\x0b\x08\x37\xb7\x5a\xa7\x8b\xdb\xdd\x3a\x4d\xea\xee\xc6\x31\xc7\x5a\xf4\xa1\x54\x4c\xbb\x0f\x39\xd4\x0c\x42\x79\x3a\x92\x43\x27\x10\xda\xd3\x9b\x0c\x7a\x0b\x81\xed\x2e\x65\x30\x0f\x10\xc6\xd3\xaf\x0c\x78\x03\x81\x3d\x9d\xcb\x80\xd7\x1a\x30\xde\xfa\x15\x84\xf1\x74\x33\x03\x5e\x42\x60\x4f\x5f\x33\xe0\x05\x04\xb6\x3b\x9c\xc1\xe8\x1d\x34\xd2\x76\xad\x9f\x46\x9a\xae\xf5\x12\x9c\x3b\x15\x50\x7b\x92\xfa\x21\x2c\x14\xa6\x1e\x3d\xd0\x0c\x00\x79\xb5\xa3\x07\x4e\x00\xb0\x57\x39\xda\x93\x50\x0e\x0e\x8b\xe9\x46\x7b\x12\xba\xc1\x41\xbc\xaa\xd1\x9e\x84\x6a\x88\x00\x91\x4f\x3c\xed\x49\x68\x86\x80\xc5\xdb\xbd\x02\x20\x5e\xbd\x68\x4f\x42\x2f\x38\xac\x57\x2d\xda\x93\x50\x0b\x0e\x8b\x69\x45\x7b\x8a\xb5\x6e\x19\x69\x35\xec\x9d\x91\x46\xc3\xbe\x41\x54\x82\x9f\x5e\x93\x4a\xa1\x07\x1f\x6d\xdd\x90\xd0\x33\x1b\xda\xa3\x24\x12\x2b\xb1\xb1\x3c\xda\x22\xb0\xb6\x36\x92\xad\x36\x02\xf6\xc1\x86\xf5\xe8\x8f\x40\xda\xd8\x48\x1e\x45\x12\x48\x6b\x04\xc9\x25\xad\x95\x0d\xeb\x51\x2d\x81\xb4\xb4\x91\x3c\x3a\x26\x90\x16\x36\x92\xad\x6c\x02\x16\xeb\xf0\x51\x59\x21\xfd\x3e\x2a\x2a\xa4\xd7\x43\x43\xf9\xf7\xf1\x51\xdf\xec\xa4\x5a\x3b\x08\x32\x82\xaf\x2a\xd7\x97\x4a\x6c\xdc\xe8\x10\x33\x7d\x4d\xa6\x75\xbf\x0e\x99\x68\x90\xfa\xf8\xd0\x20\xb7\xc6\x62\xd1\x2c\x7f\xd0\xca\xf5\x71\xa0\x01\x6e\x34\x40\x5d\xf7\x35\xc0\xb5\x0e\x68\xb5\x72\xa5\x95\x2f\xdd\x8d\x5c\x6a\x80\x0b\x77\x1b\x17\x1a\xe0\xdc\x6a\xa3\x21\x78\x77\x1b\x75\xf9\xbb\x9b\x08\x3d\x28\xdd\x85\x42\xc0\x66\x1a\x98\xa7\x53\xa1\x0f\x85\x3b\x51\x36\xf8\x56\x83\xb6\xbb\x17\x78\x51\xb8\x1b\x65\x43\x6f\x34\x68\x4f\x47\x03\x3f\x4a\x73\xa4\x6c\xa0\x95\x06\xe4\xe9\x72\xe0\x49\xe1\xae\x94\x0d\xbd\xd0\xa0\xed\xce\x07\xbe\x14\xee\x4c\x21\x7d\xa0\x77\x81\xbf\x7e\xbd\xbf\xf8\xdc\x69\x40\x0d\xee\x94\xe6\x4f\x21\x50\x33\x08\xe5\x55\x95\xc1\xa1\x42\x3d\x2a\x1b\x7a\x0b\x81\x31\x45\x51\x2e\x15\xea\x53\xd9\xc0\x1b\x08\xec\x55\x13\xe5\x54\x41\xaf\xca\x86\x59\x41\x18\xaf\x92\x28\xb7\x0a\xf5\xab\x6c\xe0\x05\x04\xc6\x54\x44\x39\x56\xa8\x67\x85\x88\x5e\x93\xbc\xbf\x72\xad\x97\x10\xfd\xd0\x7d\x2b\xcc\xb9\x42\xc1\x67\x08\xb8\x47\x63\x74\xef\xca\xe7\x5e\x61\x68\x5b\x04\xcb\xd6\x21\xcd\xbf\xf2\x39\x58\x18\xd6\x06\xc1\xf2\x68\x95\xe6\x61\x21\x2e\x16\x06\xbc\x42\x80\x3d\x7a\xa6\xf9\x58\x3e\x27\x0b\xc3\x5a\x20\x58\xb6\xe6\x69\x5e\x96\xcf\xcd\x42\xfb\x12\xeb\xca\x31\xbe\xb0\xfe\x4f\xae\x0a\x1f\xdf\x23\x36\xf9\xe6\xe0\xa4\xd7\xd9\x62\x95\x7b\x9d\x2d\xc6\x6a\x90\xb3\xc5\x1a\x18\xe4\x6c\x0d\x6c\xe1\xce\x56\xdf\x82\x20\x67\xab\x6f\x75\x90\xb3\xd5\x4b\xca\xe7\x6c\xf5\x42\x0d\x72\xb6\xfa\x8e\x08\x72\xb6\xfa\xfe\xf3\x39\x5b\x7d\x57\x07\x39\x5b\xbd\x58\x43\x9c\xad\x73\x16\xe4\x6c\x29\xb0\x30\x67\x4b\x81\x87\x39\x5b\x12\xdc\xeb\x6c\x49\xa0\x30\x67\x4b\x42\x87\x39\x5b\x12\xda\xeb\x6c\x49\xa0\x30\x67\x4b\x42\x87\x39\x5b\x12\xda\xeb\x6c\x49\xa0\x30\x67\x4b\xf5\x41\x88\xb3\x25\x81\xfd\xce\x16\x83\x1a\x75\xb6\x14\x54\x90\xb3\xa5\xa0\x83\x9c\x2d\x09\xed\x73\xb6\x24\x4c\x90\xb3\x25\x81\x83\x9c\x2d\x09\xec\x73\xb6\x24\x4c\x90\xb3\x25\x81\x83\x9c\x2d\x09\xec\x73\xb6\x24\x4c\x90\xb3\xa5\x44\x1f\xe0\x6c\x49\x58\xaf\xb3\x75\xce\xae\x72\xb6\x00\xf8\x35\xce\x16\x40\xbb\xc6\xd9\x1a\xd0\x02\x9c\xad\x01\xf8\x1a\x67\x6b\xc0\xba\xc6\xd9\x1a\xb0\x02\x9c\xad\x01\xf8\x1a\x67\x6b\xc0\xba\xc6\xd9\x1a\xb0\x02\x9c\xad\x01\xf8\x1a\x67\x0b\xf4\x65\xb8\xb3\x35\x20\xdd\xe2\x6c\x19\xbb\xec\xf7\xd8\x94\x7e\xf3\xae\xb4\xd7\xdb\x62\x95\x7b\xbd\x2d\xc6\x6a\x90\xb7\xc5\x1a\x18\xe4\x6d\x0d\x6c\xe1\xde\x56\xdf\x82\x20\x6f\xab\x6f\x75\x90\xb7\xd5\x4b\xca\xe7\x6d\xf5\x42\x0d\xf2\xb6\xfa\x8e\x08\xf2\xb6\xfa\xfe\xf3\x79\x5b\x7d\x57\x07\x79\x5b\xbd\x58\x43\xbc\xad\xe2\x18\xe4\x6d\x29\xb0\x30\x6f\x4b\x81\x87\x79\x5b\x12\xdc\xeb\x6d\x49\xa0\x30\x6f\x4b\x42\x87\x79\x5b\x12\xda\xeb\x6d\x49\xa0\x30\x6f\x4b\x42\x87\x79\x5b\x12\xda\xeb\x6d\x49\xa0\x30\x6f\x4b\xf5\x41\x88\xb7\x25\x81\xfd\xde\x16\x83\x1a\xf5\xb6\x14\x54\x90\xb7\xa5\xa0\x83\xbc\x2d\x09\xed\xf3\xb6\x24\x4c\x90\xb7\x25\x81\x83\xbc\x2d\x09\xec\xf3\xb6\x24\x4c\x90\xb7\x25\x81\x83\xbc\x2d\x09\xec\xf3\xb6\x24\x4c\x90\xb7\xa5\x44\x1f\xe0\x6d\x49\x58\xaf\xb7\x55\x1c\xaf\xf2\xb6\x00\xf8\x35\xde\x16\x40\xbb\xc6\xdb\x1a\xd0\x02\xbc\xad\x01\xf8\x1a\x6f\x6b\xc0\xba\xc6\xdb\x1a\xb0\x02\xbc\xad\x01\xf8\x1a\x6f\x6b\xc0\xba\xc6\xdb\x1a\xb0\x02\xbc\xad\x01\xf8\x1a\x6f\x0b\xf4\x65\xb8\xb7\x35\x20\x8d\x79\x5b\x9d\x3a\x01\xea\x3d\x4d\x2a\x4f\x64\x13\x96\xa0\x8e\xc1\xcb\xf3\x5a\xec\x6e\xd3\x83\x7e\x86\x4b\x9e\x2d\x17\x9f\xc1\x35\x0c\xf3\xee\x02\x38\x49\xd5\x9d\x18\x5d\xe7\xdd\x60\xc5\xa9\x91\xe1\x04\x49\x7a\xe2\xbe\x65\xc5\xc9\x3c\x75\xfb\x2a\x7b\x7d\xea\x9a\xa7\x2e\x9b\x44\xd6\xb7\xd3\xf0\xed\x50\x55\x9d\x09\xa7\xbe\x9d\x78\x9a\x18\xfe\xf5\x44\x49\x66\x42\xaa\x6f\x27\x28\x32\x25\x17\xcf\x41\x66\x33\xc9\x4d\x57\xd5\x9e\x4c\x23\x59\x96\x19\xed\x33\x6a\x36\xc9\x71\xc9\x20\x97\x9b\xe6\x1e\xa2\xa2\xf7\x3f\x49\xe2\xbb\x43\xde\xc8\x1b\x40\xb0\xd9\x7e\x38\x28\xb4\xb4\x2a\x7a\x95\xab\xc7\x49\xfa\x01\xad\x8e\xd0\x8b\x9d\x64\xc7\x61\x4f\xe2\x1a\x09\x14\x7c\x82\xe8\xd2\xa7\x4e\x65\xac\x82\xa0\x1e\x71\x46\x53\xdf\xd8\x03\x89\xa6\x38\x5c\x9c\x56\x65\x46\xcb\x96\x66\x98\xf2\xa2\xa5\x40\x2a\xb0\xdc\x56\x69\xb4\xd4\x81\x6d\xab\x39\x5a\x6a\x28\xfc\xca\x18\x80\x31\x17\x92\x96\xfb\xc8\xab\xd1\x0a\x01\x6d\x3d\x52\x08\xd9\x1f\x8a\x91\xb6\x23\x85\x38\x2e\xd2\x72\xa4\xf0\x74\x43\x8b\xae\xa7\x2c\xc6\xab\xb4\x7b\x73\x53\xbc\x6d\xd7\xe4\x35\x90\xc7\xae\xec\x4e\x71\x75\x88\xbb\xd7\x9a\x7e\xa8\xb2\xec\xa3\x53\xed\xb6\xfd\x3f\x9d\x18\xbb\xac\x3c\x90\xf2\x5f\x90\x66\x97\x25\xb4\xc9\x25\xad\x8a\x1f\xd3\x82\xb4\xed\xef\x7f\xe8\xe7\x26\x7e\xc7\x12\xcb\x1e\xa4\xae\x88\x48\xb5\x2a\x2e\x67\x76\x99\x54\x2c\xb2\xc1\xad\x12\x4e\xb9\xcb\x34\xc2\x93\x48\x7c\x3e\xdd\x56\x1f\xe5\x77\x43\xec\xda\x8c\x09\x62\xca\xf3\x23\x61\x73\x87\x2a\x3a\x21\xd3\x4a\x26\x4a\xa1\xb1\x1a\xf4\x75\xaa\xb2\x2e\xe9\xd3\x0c\x56\x9b\x59\xa4\xd9\xbf\x41\xc7\x5d\x24\xb1\xda\x84\x9a\x81\xda\xec\xb9\x0d\x6b\xdd\xa0\xbb\x2e\x92\x43\x6d\xd2\x96\x8e\xa8\x0d\xaa\x76\x82\xc4\x4e\x7c\x1d\x46\x8a\x17\x0c\x8e\x64\x4c\x8d\x9f\x0c\xa6\x0d\xa0\xa1\x55\x1c\xdc\x49\x6d\x68\xe3\xc8\xd5\xfe\x87\xfe\x9f\x43\xad\x44\x26\x05\x54\xaf\x54\x19\xae\x58\xa2\xd8\xa1\x59\xb2\xd4\xd2\x1f\xac\x46\xab\xcc\xa5\x5c\x2e\xaa\x68\x8d\x52\x83\x40\x8d\x88\x7e\x61\xad\x04\x0a\xe6\xa2\x0a\x6a\x74\xab\x98\x96\xb9\x02\xd7\x1d\x2d\x95\x85\x47\xc7\x0c\xb8\x71\x25\x33\x18\x47\xb4\x4c\x23\xe9\x55\xb3\xa0\x7c\x1b\x59\x42\xb7\xe9\xda\xa1\x67\x79\x79\xa8\x50\x25\xe3\x05\xb8\x86\xf5\x65\x0e\xf5\x62\x45\x96\xfe\x58\xb5\xe8\x05\x2e\xad\x42\x89\xd9\xb5\x48\x8d\x91\xb5\x20\xca\x64\xb5\x06\x68\x12\x4a\x4c\xd6\xe2\xd1\x21\x98\x99\x04\xd7\x8d\x21\x55\x89\x47\x81\x20\xd0\xb8\xf6\x40\x66\x11\xd5\x19\x88\x79\xf5\x66\x3c\x87\x4a\xba\xa4\x8b\xc3\xc2\xa1\x34\x22\x3d\x0a\xaa\x37\xaa\x0c\x57\x1d\x51\xec\xd0\x1e\x59\x6a\xe9\x09\x56\xa3\x55\xe6\x52\x23\x17\x55\xb4\x46\xa9\x31\xa0\x46\x44\x9f\xb0\x56\x02\x95\x72\x51\x05\x35\x7a\xe6\x3f\x3d\x59\x16\xa6\x33\x5a\x7e\x1a\x8f\x6e\x19\x70\xe3\xea\x65\x30\x8e\x68\x98\x46\xd2\xab\x64\x61\x49\x74\xc8\x61\x9e\xa6\x0e\x3d\xe3\x09\x72\x50\x35\x93\x45\xb8\x96\xf1\x52\x87\x92\x89\x42\x4b\x8f\x90\xda\xcc\x22\x97\x86\x39\x48\x62\xb5\x49\x0d\x1a\x6a\x43\xd4\x0b\x69\x1d\xd0\x2e\x07\xc9\xa1\x36\x8f\x6e\xc1\x24\x44\xb8\xce\xc0\xac\x44\x1e\xd5\xd2\xc1\xc6\x35\x4b\x67\x1a\x51\x2c\x48\xd0\xab\x57\x41\x69\x93\xf6\x69\x6a\xa8\x15\xc8\xe8\xcb\xb0\xe0\x6d\xc4\x69\x32\xfb\x9d\x76\xc9\xf9\x05\xbb\x78\xcc\xdf\x6e\x8d\x48\x99\x45\x1f\x86\x40\xd3\x66\xbd\x51\xdb\x91\x68\x45\x66\x8c\xca\xca\xa3\xb4\x32\x72\xe7\xc4\xaf\x5a\xf6\x9c\xf8\xdc\xaa\xb4\x38\x32\x29\x43\xff\xad\x67\xf0\x94\xb3\x18\x1b\xbf\x90\xbd\x27\x2c\xa3\xb2\x73\xc1\xc9\x63\x82\x26\x8f\x4f\x20\xf6\x80\x64\x6d\xf1\x22\xa1\x0b\x70\x1f\xd4\x89\x6f\xb7\x3a\x01\x91\x35\xb9\x0f\x6a\x8c\x1c\xb2\x98\xf6\x41\x9d\x7a\x6a\xbc\xcb\xdc\xf9\x86\x1c\x64\xf4\x28\x06\xcc\xac\x1b\x86\x06\x45\x09\xc3\x4e\xde\x16\xa2\xc1\x8e\x5b\xb0\x07\xb9\xbf\x09\xfb\xa6\xba\x87\x4e\x7a\x13\xb6\x56\x37\xec\x04\x3d\xd2\x7d\x65\x4f\x80\x8c\x47\xd7\x77\xc4\xf5\xc8\xa0\x1f\xde\x80\x7c\x4b\xcd\xa0\x17\xde\x80\x0c\x6b\xd6\xfa\x40\xec\x91\x5e\xdf\x09\x80\xa0\x98\x2c\x6f\x45\xf6\x1b\x0b\x5b\x8e\xb7\xd5\x8c\x21\x9f\x34\x51\x18\xc6\x95\xcd\x30\x87\x9c\x16\x59\x4b\x3b\x35\x33\x89\x39\x23\x71\x67\xfc\x4e\xb0\x04\xde\x05\x3d\xd2\x92\x4b\xde\x4e\xb2\x62\xcc\x43\x18\x59\x5f\x5a\xda\xf9\x0c\xd9\xa9\xb0\xb3\x95\xe8\x39\x6e\xf4\xac\xe8\x58\xea\xc1\x55\xff\x4f\xb2\x4f\xf6\xf4\xfa\xb4\xf6\x06\xeb\x2b\x24\x7b\xee\x90\x13\x8f\x3d\xd1\xf0\x63\xf7\x5a\xd3\x1f\x5a\x4a\x9a\xf4\xc4\x63\x82\xbf\xee\x8b\x18\xa0\x52\xf6\xee\xf4\xbe\x7a\xf9\x49\xbc\x16\xc1\xbf\x36\x24\xcb\x2b\xce\x89\x4a\xde\xc8\xf2\xf7\xc0\xbe\x51\x3b\x3d\x7f\xd9\x5a\xfd\xc0\xb3\x34\xd9\xd5\x1d\x72\x99\xc9\x4e\xd7\x07\x1b\xb2\xe9\xdd\x2a\x0c\xf4\x11\x4d\x65\xc0\xdf\xb8\xf8\xf1\x7c\x29\xba\xbc\x66\xa9\xf3\xc4\x97\x5e\x59\x38\x19\xe4\xa9\x03\x93\x33\xf9\x2c\x05\x26\x20\xa4\x8c\x8b\x89\x17\x08\x27\xf7\x57\x7f\x85\xa2\xba\x74\xf5\xa5\x73\xc8\x45\xdb\xf8\xdc\xd8\x59\x8a\xc2\x9f\x19\x59\xad\x54\xc8\xf3\x50\x35\xe7\x38\xad\xca\xae\xa9\x5c\x99\xef\x1c\x0f\x3b\x2c\x96\xc6\x1b\x01\xeb\xfa\x85\xa5\xa8\x7e\x13\x5f\xae\x5c\x48\x56\x96\xab\xfc\x4c\x8e\x14\x66\x81\x0a\x4f\x91\x34\x96\xff\xaa\xa7\xd1\xff\x7f\x23\x9f\x55\xb2\x71\x67\xc0\x1a\x41\xc1\xde\xa1\x10\x7c\xb1\x16\xc2\x37\x24\xa2\xe9\x6c\xd5\x4e\x22\x9b\xc1\xde\xac\x9b\x70\x8f\xf6\xcb\x15\x23\x74\x07\x7a\xfa\xc3\x15\xef\xde\x49\x7a\x6f\x26\xc6\x0a\x31\x05\x03\xaf\x38\x40\xc2\x2c\xaf\x31\x39\xd0\xad\x36\x96\x9c\x89\xbf\x02\x64\x3e\x61\xa9\xbc\x58\xca\xf5\x77\xef\x58\xf9\x2c\x99\x4f\xa2\xd9\x66\x35\x89\xe6\x8b\xc5\x24\x9a\xae\x6f\xe9\xca\x10\xb2\x68\xbb\x77\xcc\xa0\xd7\x05\x49\xe9\xa9\x2a\x32\x23\x03\xf3\x76\xcb\x5b\x5e\x93\x34\xef\x5e\x77\xd1\x0c\xa5\xd1\x2f\xc3\x98\x79\xf2\xd1\x71\xd4\x2e\xa4\x78\x13\xfa\x8f\x59\xce\x9e\xe0\xc8\x7e\x9a\x44\x7a\x41\x43\x49\x56\x95\xc5\xeb\x4f\x93\x48\xfa\x14\x03\xb0\x0e\xeb\x8e\x12\x88\x14\x91\xfe\xc6\x43\x1e\xc6\xaa\xe2\x6d\x12\xa9\x54\xcb\xaa\x8b\x49\x51\x54\x5f\xa9\xdc\x04\x95\xef\x24\xd9\x38\xfe\x29\x04\x9b\xc0\x49\x5d\x53\xd2\x90\x32\xd5\xd3\xfb\x22\x4b\x78\x89\xd1\xbb\x5b\x19\x7d\xce\x53\x1a\xd7\xf9\x0b\x2d\x62\xf6\xf8\xd1\x2e\xe1\x6b\x7a\x50\x5d\x46\x3a\xaa\x4f\xdb\x5d\x7e\x36\xbe\xf4\x30\xfd\xd7\xb8\xa8\x52\x52\xe8\x65\xe7\xaa\xec\x4e\x3f\xa9\x35\xa6\x66\x7c\x17\x4b\x99\x16\xec\xdd\x94\xab\x04\x3b\xe3\x10\xb7\x67\x93\x7a\xdf\xe5\x6e\x08\xce\x11\x33\x1d\x7e\x32\x1a\x93\x1e\x58\xce\x34\xa3\x68\x32\x22\xb0\xda\xb3\x2d\x13\xbc\xc8\xa8\x77\x00\x32\xa8\xf3\x3a\x55\x71\x84\x88\x2b\xc1\xc5\x55\x1c\xc7\xc4\xa5\x43\x38\xc4\x65\x93\x71\x8b\x4b\x87\x1d\x17\x57\x71\x74\x8a\xcb\x28\xc2\xc5\x55\x1c\xbd\xe2\x2a\x8e\x88\xb8\x96\x6b\x3d\xe9\x1c\x1b\x67\x8c\x7d\xe0\x72\x6a\x01\x29\x95\x84\x42\x78\x63\x93\x68\xca\x9c\x2f\x63\x03\x1d\xa6\xf7\xc2\x9e\xbf\xba\xea\xa5\x12\x59\x57\xc4\x96\x01\xaa\xc6\x61\x51\x00\x43\x76\x72\x81\xa2\xa7\x0a\x73\xbc\xfc\x92\x58\x0b\x02\xf1\xee\x8c\xe3\x01\x21\xc5\x09\xee\xb8\xab\x72\x99\x7f\x1c\x05\xeb\x75\x93\xb7\xc0\xf2\x60\x65\xdb\x10\x7c\xb0\x12\xc0\x1f\x87\x82\x42\x5d\xaa\xc5\x80\x9e\xb7\x6f\x8e\x48\xf5\x93\xd5\x97\x9f\x40\x97\x42\xaa\x31\xd2\xff\x82\x53\x93\xf1\x00\x6d\x70\xe5\x4e\xbf\xad\xc3\xf0\x07\xdf\x02\xba\x52\x70\xfc\x69\xa4\x45\x9f\xec\xf6\xb9\xf2\xb6\xcb\x63\xba\xe0\xbd\x3a\x6b\x16\xc4\xf5\xc7\x0f\x28\x54\x44\x9f\x7b\x14\xf6\x54\xe2\xe0\x8b\x26\x38\x2b\x5b\x34\x07\x5c\x9d\x38\x2f\x05\xdc\xf8\xa7\x6b\x53\x84\x80\x25\x4d\x7a\xe0\x3b\xe6\x20\x98\x1d\xd1\xf7\x38\x02\x66\xf5\x47\x00\x63\xaa\x66\xdd\x94\x58\x9f\x7d\x6c\x09\x18\x1f\x57\xc0\x34\x79\x98\x82\x4e\x4d\xcc\x4f\x1c\x59\xd6\xcc\x58\xb8\x69\xeb\x49\xf3\x20\xed\x06\x1f\x31\x9e\xea\xc0\xf4\xe2\x29\x16\x13\xad\x91\x92\x31\x41\x92\x8e\xcb\xaa\x34\x3c\x38\x35\x3f\x1a\xe7\xfd\xd0\x0c\xbe\x73\x74\x0d\xba\xc2\xf3\x23\x6b\x51\x07\x7f\xc5\x88\xa3\x80\x46\x2c\x80\x8f\xa2\x1c\x50\x94\x30\xcc\xf7\x35\xcc\x9c\x3d\x94\xdb\x61\xfd\x2d\xa4\x70\x2d\x2f\xc1\x82\xf1\x10\x06\xb2\xf2\x40\x89\xa5\xc5\x95\x02\x84\x83\xc3\xe4\x5d\x1b\x2c\xf3\xb7\xc9\x56\x57\x5f\xe1\x30\x41\x57\xe9\xd1\x7a\x72\x41\x7b\x92\x6b\x78\x3d\x0c\xa1\x2e\x8e\xe0\x23\xfd\xb7\xc6\xb5\xd8\x51\x3f\xe2\xbf\xf9\xb4\xb8\x1f\xdb\x86\x16\x1b\x84\x1d\x9d\x50\x1c\x3d\x9a\xf3\x1b\x0a\xe3\x5a\x96\x82\xe5\xe3\x21\x8c\x2b\xb3\x09\x15\xa6\xcc\x26\x16\xa6\xcc\x92\x77\x4d\x99\x1f\xee\x22\x62\xc9\xd1\x89\xb4\xf1\x81\xd2\xac\x5f\xd5\x3b\xbc\x33\x14\xd4\x16\xb8\x31\x0f\x2c\xe7\x53\xe0\x16\x6a\x2d\x75\xd4\x07\x9d\xd6\xc1\x79\x82\xd3\xca\xcf\x71\x5e\x66\xf4\x65\x17\xcd\xf1\xf5\x83\xbc\xfe\xb9\xd4\x1f\x42\x5d\x60\xd1\x4b\xf9\xd1\x7e\xfe\x8d\x49\x97\x3b\x85\x31\x7d\xa6\x65\xd7\xea\x07\x7a\xe5\x68\xf9\xe4\x69\x95\xbc\xb1\xba\xd6\x39\x19\x53\x46\x35\x9f\x04\xd0\x96\x96\x2e\xd4\x6a\xb3\xfe\x93\x2f\x74\x41\xdf\x1e\xfd\x3e\x78\xbb\x7a\xb1\x60\x48\xf9\x3a\x7a\xf1\x89\x16\x35\xf7\xdc\x4d\x44\xe6\xff\xa0\x1f\x81\x3b\x07\x4b\xed\xd5\x1d\x56\x28\xfd\x3c\x94\x1f\xe8\x74\x19\xf8\x9a\x93\x08\xbc\x32\xec\xd1\x39\x9d\x67\x4b\xef\x8d\x28\x28\x47\xfd\x6d\xc3\xd3\xa3\x8c\xba\x83\xb6\xfc\xc1\xbc\x37\x87\x69\xd7\xf5\x4b\xf4\xdd\x7a\xb3\x9f\xad\x1f\x6e\x8e\xc9\x02\x1a\x68\x83\x60\x10\x85\x64\x99\xb8\x4a\x66\xcb\xdd\x79\x7e\xf7\xd1\xd5\x5b\xa3\xf2\xd3\x07\xa0\x1b\x59\x3e\x1c\x67\x0e\x2f\xeb\xbb\x3e\xbc\x86\x62\x74\x78\xa9\x62\x6b\x78\xa9\x12\x38\xbc\xf4\x8f\xe6\xf0\x12\xa5\xf8\xf0\x32\x0b\xf1\xe1\x25\xa1\xec\xe1\xa5\x95\xb8\x87\x97\xf6\x5e\xa2\xce\xf3\xd8\xf0\xe2\xa8\x7f\xbf\xe1\x85\x32\xea\xdb\x13\x59\xcd\xee\x35\xbc\xd2\x84\xcc\xd6\xfb\xb7\x0d\x2f\x4e\x03\x6d\x90\x7f\x78\x0d\x72\x77\x9e\x40\x45\x86\x57\x60\x47\xe3\xc3\xcb\x46\xa6\x4d\x53\x35\xd6\xe0\x32\xbe\xea\x43\x4b\x16\xa2\x03\x4b\x14\x5a\xc3\x4a\x7c\x87\x83\x0a\x7e\x32\x87\x14\x2b\xc3\x07\x94\x5e\xa4\x0f\x27\x08\x01\x87\x92\x4e\x76\x64\x28\x69\xcf\x84\x42\x4e\xc7\x06\x12\x47\xfc\xfb\x0d\x24\x84\x4d\xf7\x30\xe2\xef\x9b\xde\x69\x18\xd1\x87\xe5\xc3\xe2\x8d\xc3\x88\xd1\x40\x9a\xe3\x1f\x44\x83\xcc\x9d\x47\x6d\x91\x41\x14\xd4\xc5\xf8\x10\xb2\x51\x15\x18\x57\xa5\xff\xed\x21\xc2\x2f\xa1\xae\x74\x07\x52\xc7\x97\x8f\xfc\x8f\xd3\x51\x31\xa0\x61\xb0\x39\x8e\x2a\xc0\xf0\xe9\xca\xb9\x07\x00\x8f\x1c\x6c\x16\xfd\xbf\xc0\xe4\xd6\x8c\x4f\x31\x9c\xcc\x7d\x0d\x77\x10\x1a\x8d\x37\x3b\x82\xcb\x72\x9f\xc9\xae\x09\x0e\x47\x4f\x5d\xc2\xc1\x97\x4f\xf3\xdc\x5a\x0d\x5c\x51\xe2\xb5\xa1\x24\x80\x0a\xe3\xa8\xec\x04\xdb\xb5\x8c\x41\xaa\xc6\x9a\xda\x03\x68\x0d\x27\xbe\xf5\x16\x86\xb0\xef\x4a\xed\x3c\x35\xcc\xa7\xe1\xa2\xf0\x84\x74\x14\x92\x84\x49\x43\xd7\xe6\x16\xfc\x58\xf4\xb5\xd2\x02\x33\x9c\xf6\x7d\xd8\x7f\x19\x57\xd5\x61\x09\x7d\x37\x76\xd4\xc4\x85\x30\x05\x5a\x6f\x45\x7f\x7d\x24\x1d\x3b\x66\x48\x0d\xd6\x06\x84\xda\x19\xc7\x77\x94\xd0\xc4\x15\x06\x1b\xee\x98\x86\x6e\xc3\x94\x11\x33\xf6\x44\xc1\x4b\xb2\x56\xa7\x61\x65\xd0\x75\xb0\x80\xa4\xbf\x81\x17\x60\x6f\x17\xc3\xb0\xff\xd8\x73\xc1\xc9\x4d\x8c\x83\x1d\x3f\xb8\x99\xba\x31\x42\x3b\x10\x05\xdf\x2c\x0e\x7e\x38\x2c\xc8\x80\x6b\xec\xdb\xe3\x0f\x93\x0f\x3e\x0a\xac\xb7\x50\xbd\x5d\x1c\xaa\x2e\xe0\x79\xaa\x5b\x5b\x65\x84\x0a\xc7\x1a\x39\x5b\x4e\x17\x8b\xd1\x17\xe7\x42\x6b\x6c\xcf\xe3\x35\x1a\x47\x04\xa4\xb5\x1d\xdb\xc7\xd5\xcf\x18\x3a\xb7\x71\xf5\x73\x87\xd8\xce\xae\xe7\x2c\x22\x1e\xd3\x43\xee\x68\xf8\x77\x88\xe3\x73\x1b\x77\xd5\x25\x3d\xc5\x24\xe5\xe6\xe5\x4c\xca\xbc\xbe\xf4\x26\xa6\x2a\x79\x98\xd2\x57\x6a\xec\x30\x43\xf7\xf5\xd2\xd2\x26\xe6\xa1\xe7\xe1\xfc\x23\x3b\x60\xe6\x28\x69\xf1\x02\xf4\xe3\x35\x27\x2c\xfd\xaf\x3e\x0e\x6f\x51\xf6\xfd\x2b\x6e\xa9\x4f\xc5\x39\x5b\xf0\x69\x07\x3f\x81\xdf\x3b\x0d\xa3\xd7\xba\xe1\x23\xc4\x01\x9e\xff\xaf\x7e\x3e\xd7\xe2\x11\xfe\x1e\x2e\x89\x19\xa7\xd3\x99\x4a\x65\x34\xad\xd8\x61\xaf\x52\x8f\x08\x0f\x82\xd0\x9a\x6d\x5e\x36\x33\x3a\x23\xfc\x94\xe4\xa2\x7e\x61\xcd\x36\x16\x25\x33\xcf\x73\xae\x1e\x14\xc8\x35\xd8\x89\xdf\x77\xe5\xd8\x11\x3d\x39\xc6\xb1\xe8\xf8\x23\xbe\xa9\xcd\x46\x6f\x5e\x74\xbd\xd6\x91\xa2\x3e\x91\x0f\xe2\x94\x60\xf4\x43\xb4\xd6\x0f\xd6\x86\x3c\x0c\xab\x1d\x33\x9c\xae\x57\xa0\x2d\x71\x46\x0f\xe4\x52\x74\x58\xef\x79\x4e\x23\xeb\x6b\x2d\x70\x09\x10\x90\xd4\x3a\x57\x7d\x04\x7a\x23\xbf\xed\x40\x92\x0e\xf8\x19\x03\xe5\x17\x17\xa3\x69\x55\xd3\xf2\x69\x9a\x35\x55\x9d\x55\x5f\xfb\xf9\xfb\x78\x2c\x28\x84\x54\x8f\x51\x8f\x35\x89\xae\xfb\x7f\xd8\x0a\x32\xeb\xff\x85\x36\x6c\xa7\x3e\x86\xb2\x86\xeb\x38\x56\x9f\xae\x6f\xd6\x67\x5c\xd0\xaa\x54\x33\x23\x56\xf1\xce\x8b\x8c\xf5\xc1\x50\x38\xdc\x22\x05\xc5\xda\xa9\x59\xf4\x3b\xca\x2f\x28\x46\x19\x1e\xca\x51\x8e\x41\xf1\x18\xba\x50\x21\xc7\x50\x95\x08\xa3\x00\x20\xb5\xcc\x18\xa4\x10\xe2\x08\xd8\x2e\x98\xe0\x2e\x90\x20\x6b\xa9\xe7\x9a\xf2\x15\x83\x39\x9a\xee\x49\x76\xa4\x9a\x99\xf0\x3e\xc0\x0d\x69\xd4\x4d\x7e\x26\xcd\x6b\x30\xf2\x86\xec\x37\x08\x67\x73\xba\xce\xc8\x12\x21\xac\x2b\x94\xfc\x08\x55\x57\x7c\x33\x2d\x8d\xfc\x8c\x81\x8e\x5b\x1a\x01\x69\x58\x1a\x77\xc3\xe6\x0f\xeb\x64\x9b\x60\x0d\x4b\x96\xd9\x26\xb8\x61\x21\x96\x46\x67\x6d\xdc\xd2\xc8\xfa\x0c\x4b\x63\x7e\xc6\x05\xed\xb0\x34\x66\xf1\xce\x8b\x8c\xf5\x81\xcb\xd2\x88\x62\xcb\xd2\x58\xdf\x51\x7e\x9d\x96\xc6\x2a\x47\x39\x76\x5a\x1a\xbb\x7c\xc4\xd2\x08\x84\x51\x80\x00\x4b\x63\xe8\xfc\x08\x58\x80\xa5\x31\x46\xc6\x18\xd8\x88\xa5\xb9\x7e\x48\x63\xf6\x06\x50\xf1\x66\xef\xeb\xc9\xc8\x0d\xc9\xb0\x91\xb9\x4a\xf7\x0f\xab\x14\xe1\x6f\x99\x12\xba\x4c\x11\xc2\xba\x66\xc9\x8f\x50\x87\x65\xd6\x20\xc3\xe4\xc8\xcf\x18\xe8\xb8\xc9\xd1\x73\x2e\x8d\x37\x6c\xb9\xdc\x66\xcb\x25\xb6\x85\xbb\x7d\x58\x2e\xb6\xa1\x0d\x0b\x31\x39\xce\x74\x50\x0e\x93\x23\xeb\x33\x4c\x8e\xf9\x19\x17\xb4\xc3\xe4\x98\xc5\x3b\x2f\x32\xd6\x07\x2e\x93\x23\x8a\x2d\x93\x63\x7d\x47\xf9\x75\x9a\x1c\xab\x1c\xe5\xd8\x69\x72\xec\xf2\x11\x93\xa3\xd2\x62\x8d\x00\x04\x98\x1c\x43\xe7\x47\xc0\x02\x4c\x8e\x31\x32\xc6\xc0\x46\x4c\xce\xf5\x43\x1a\x33\x39\x80\xca\xa8\xc9\xc9\xcb\x43\x15\x6a\x6f\xf6\x69\x82\xee\x5a\x2d\xd7\xfb\x87\x8c\x98\x54\x75\x9d\x62\x5f\xa0\xea\xb2\x3c\x53\x16\x88\xa1\x2a\x20\x67\x95\x6f\x14\x5f\xd1\x88\xc5\x6c\x9f\x64\x2b\xcc\xa8\xaf\xb7\x64\x9f\x8e\x37\x22\xc4\xaa\x28\x7e\xc6\x0d\x0a\xab\xc4\xb0\x26\xda\x37\x44\x8c\x0e\x3b\xa2\x95\xd9\xa2\xc5\x2d\x88\x5e\xa2\x9b\x8f\xbe\xcc\xb2\x1d\xfa\x47\x9b\x3b\xa7\xd5\xd0\x0b\x6d\xfe\x2c\x7b\x81\x16\x29\x0e\x5d\x23\x8d\x67\x41\xf3\x95\x06\x98\x09\xa8\xad\x3e\x98\x00\x03\x01\xb4\xda\x4f\x6a\xcc\x34\x5c\x37\xfa\x50\xbb\x30\x90\x18\xb5\x0b\xf2\xf0\x46\xd8\xa8\x3a\x24\x24\x5b\x62\xcc\x51\x4a\xe6\x8b\x35\x42\x58\x57\x1c\xf9\x11\x76\xbc\xcc\x11\x66\xb8\x22\xf2\x33\x06\x3a\x6e\x29\xf4\x0c\x6b\xe3\x0d\xa3\xe9\x76\x33\xc3\x16\x9c\xd9\xea\x61\x35\x9b\x87\x36\x2c\xc4\x68\x38\x93\xbf\x39\x2c\x87\xac\xcf\x30\x1e\xe6\x67\x5c\xd0\x0e\x13\x62\x16\xef\xbc\xc8\x58\x1f\xb8\x6c\x89\x28\xb6\xcc\x89\xf5\x1d\xe5\xd7\x69\x54\xac\x72\x94\x63\xa7\x2b\x62\x97\x8f\xb8\x22\x2a\x09\xde\x08\x40\x80\x8d\x31\x74\x7e\x04\x2c\xc0\xd2\x18\x23\x63\x0c\x6c\x2c\xce\x72\xf5\x90\x46\xa3\x2d\x03\x95\x51\x93\xc3\x93\xb7\x05\x5a\x9c\x6c\xbb\x5a\x2c\xd1\x81\xb9\x5c\x1c\x16\xc4\xa6\x6b\xc4\xef\xf8\x37\x2d\x50\xc8\x73\xc7\x21\x60\x66\x70\x0e\x66\xa3\xf3\x46\x4e\xaf\x69\x50\xba\x5d\x24\x73\xcc\xf5\x23\xe9\x7c\x3b\x5f\x85\x35\x28\x28\x9e\xeb\xc8\x03\xe8\x0a\xe7\xf2\xca\xcc\x68\xae\xfe\x15\x15\xae\x2b\x96\xab\x97\x62\x02\x77\x44\x72\x8d\x32\x23\x90\xcb\x4a\xed\x38\xae\xf1\x19\xe3\xd4\x1d\xc5\x35\x8a\x31\x5e\x1d\x6e\x8b\x55\x38\xea\xb8\xc8\x2c\x88\xfe\xf2\x90\x00\xae\xa6\xdb\x7e\xa8\x90\xf0\x2d\x1c\x03\x63\xe4\x46\x8c\xca\xd5\xa3\x16\xb3\x29\x80\xc8\xa8\x4d\x29\xf2\x92\x6f\xd3\xa3\x37\xbc\x5d\x81\x1e\xb9\x27\x9a\x98\xa4\x44\xe7\xf6\x3f\x75\x45\xea\xbf\xec\xac\x2f\x40\x1b\x3d\x02\x56\x3c\x7a\x9f\x56\xb9\x76\xfb\xcc\xc3\xba\xcd\xa8\xa6\xba\xec\x03\x48\xa1\xa9\xf5\x94\xfd\xd8\x4b\x10\x11\xb5\xca\x5a\xac\xe6\x9b\x14\xdd\x65\xbd\x94\x19\x6d\x8a\xdc\xda\xd7\x1e\xaf\xd8\x31\x04\x8d\xa2\x91\x99\x1d\xb4\x60\xa4\xaf\x90\x66\xc1\x17\x6b\x3c\x3b\xc7\xea\x90\xc7\x53\xff\x97\x64\xf3\x08\xcf\xf9\xfc\x2a\xf7\xe8\x40\xdd\xed\x19\xd6\xad\xdf\xeb\xbd\xef\x4d\x54\x50\xe9\x4b\x0b\x2b\x7d\x69\x8d\x06\xf3\xfd\xeb\xbb\xd5\xe9\x3b\x71\x6a\x27\x2b\x1b\x50\x3e\x19\xd8\xc6\xe9\x54\x3b\xc7\xcc\xfe\xd2\x75\x55\xf9\xd3\x80\xa6\xdf\xab\xa7\x2d\xed\x5c\x85\xed\x65\x7f\xce\x61\xa9\xb0\x99\x08\x7f\x07\x92\x71\xfb\xa7\x36\xc3\xb5\x43\x04\x30\xfd\x93\xdc\x6f\x9f\xce\x56\x6d\xd4\x0b\x8e\x67\x7d\x35\x32\x4e\x39\xa0\x46\x40\x20\x3b\xd3\xbc\xd4\x39\x52\x59\x80\xd2\xaa\x28\x48\xdd\x52\x5d\xfc\x70\x18\x48\x08\x49\x03\xcd\x3d\xd7\x35\x6e\x38\x91\x67\xb1\xfa\x2a\x61\xf7\x55\xf6\x1a\x00\xce\x75\xd1\x60\x43\x2e\x71\xf1\x63\x85\x52\xed\x98\xc0\x65\x9e\x5d\x98\x80\xd7\xee\x84\xb8\xcb\xcf\x79\x79\x8c\x0f\x97\x52\x1c\x15\xa2\xa4\xa5\x56\x2f\xb8\xc1\x82\x48\xd9\xd5\x66\x17\x69\x73\xa6\x0b\x24\xcf\x18\x52\xee\x47\xb6\x6b\xa8\x9b\xaa\xa6\x4d\xdf\xdb\x5c\x2c\x93\xe8\x39\x6f\xf3\x7d\x5e\xe4\xdd\xab\x5d\xdf\x18\x74\x20\xa8\xea\x2e\xd2\xd0\xce\x7f\xe8\x0c\xa6\xc5\xd4\x3a\x4e\x7f\x6c\x8d\x5b\x15\xf7\x49\x30\xf8\x2c\xd3\xb2\x7e\x89\x32\xd2\x9e\xf8\xd9\x16\x3d\x5d\xe9\x72\xe4\x5c\x95\x78\x47\x0e\x83\x92\x8d\x92\xde\xf9\x24\x62\x3f\xc5\x21\x4a\xf7\xad\x5c\xc3\x9b\xc7\x4e\x52\x25\x16\xec\x99\x96\x97\xb1\xdb\xb7\x32\x57\xa0\x38\x3e\xfb\x08\xef\xdf\xce\x12\xee\x2c\x69\x83\xf9\x71\x78\xff\xa6\xc7\x79\xd4\x33\x93\xce\xd6\x58\x86\x03\x2d\x43\xe9\x7c\xc8\x60\x89\x9c\x01\x34\xdf\x76\xe3\xf3\x40\xdb\xc9\x14\xd7\xc8\xe9\x37\x63\x81\xa5\x3c\x25\x00\x51\xe4\xf5\x2e\x1a\x32\x66\xbc\x98\x14\xd0\xf2\x91\x3c\x85\xb0\xc4\x3c\x7f\x25\x4f\x6c\x85\xe6\x33\x4c\xd4\x71\x49\x8b\x14\x72\x07\xc7\x0f\x8d\xaa\xc1\x94\xbd\x4c\xca\xf4\x17\x1e\x5d\x4d\x86\xae\x87\x17\xd8\x75\x0d\x9a\x66\xf9\x73\x2e\x53\xd0\xa9\xe9\x18\x9e\xec\xdc\x45\x5b\xd9\xcb\x98\xa9\xc4\x82\x6b\x30\xbf\xab\x5e\xdf\x53\x91\x3f\x11\x7f\x62\x4d\x36\xef\xab\x24\x41\x69\x41\x49\xc3\x1e\xac\x3b\xdd\x70\x8a\xd4\x38\x70\x85\xa6\xf8\x76\x71\xa9\x7c\x4e\xa4\x08\x71\x80\xd7\xfd\x3f\xa7\xb3\xe8\xd2\x6a\xed\xdd\x23\xbd\x22\xf9\xda\x12\xb1\x58\x50\x25\x0e\x16\x87\x72\xb8\x6c\x33\x46\xd2\x35\x6c\x0e\x0b\xa9\x11\x83\xf4\xa4\x56\xf1\x18\xdb\x43\x19\x88\xbc\xb8\x61\x50\x77\x7c\xb4\x5a\x97\x4c\x50\xc2\x4e\x21\x38\x8e\x41\x8e\xae\xe4\xdc\x47\x45\xe5\x09\xca\xba\xa9\x8e\x79\xb6\xfb\x2f\xff\xf3\x8f\x7d\xf9\x9f\x7b\xf4\x43\xd5\x9c\xa7\x7f\xca\xd3\xa6\x6a\xab\x43\x37\x3d\xf6\x36\x85\x96\xdd\x07\x5a\x32\x8e\x7f\x38\x90\xa2\xa5\x6a\xe8\x1b\x11\x20\x35\x0f\xa0\x0e\x17\x87\x26\x21\x93\xc9\x6d\x06\x84\xcd\x87\x10\x49\x5e\x79\x32\xf2\x06\x29\xa4\x13\x25\xd2\xdc\x04\x9a\x80\xf1\xc5\x03\x36\xe4\xc5\xd2\x2d\x64\xc8\xf7\x9d\xd6\xff\x61\x4c\xa7\x87\xfc\x85\xf7\x3a\x9e\xc9\x42\x3b\xf0\x8e\xcd\xb0\xdb\xad\x6a\xfd\x60\xa0\xb1\x9e\x1b\x17\xf7\xa5\x8e\xb8\xab\x34\x89\xa6\x25\x79\xde\x93\x26\x66\xdc\x89\x53\xf7\x83\xb2\x47\xc0\xa3\x4a\xab\xb2\xa3\x65\xb7\x8b\xde\xbf\x37\x1d\x20\x2c\x41\xb7\x72\x69\xcc\x8a\x35\x86\xc7\x19\xb0\xdb\xc7\xaa\x94\x7a\x31\xdc\x00\xc4\xdf\x6a\x0d\xbe\x6e\x21\xd8\xe0\x5a\x8b\xd4\xaa\x89\x15\x79\x8f\xd9\x83\x3f\x28\xb5\xa9\xd5\xef\xec\x27\x74\x87\xf5\xb0\x58\x07\xf3\xb5\xb1\xf4\x44\x6f\xc9\x97\xe7\xba\xd7\x65\xae\xbf\x25\x1c\x5c\x85\x8b\x3b\x70\xc3\x39\x6f\xac\x66\xe0\xe3\x8d\x50\xd5\xc3\x61\x08\x80\xb9\x8f\x87\x81\xc0\xe8\x0d\x56\xae\x47\x7c\x87\x16\x20\x95\x6b\x75\x5a\xdf\xf5\x30\x2f\x28\x18\x4c\xfe\x90\x80\xc6\x6a\x39\x23\xf7\xc9\x94\xe5\xf0\xd5\xee\x65\xf0\x93\xe3\x69\x55\x9b\xa5\xf6\x8d\x2a\x79\x75\x4a\x0f\x74\x74\x55\x55\xec\x49\x83\x00\xae\x70\x40\x8b\x39\x55\x60\xde\x04\x75\x74\xbc\x80\x87\x7a\x04\x3f\x61\xb4\x9f\x2c\xda\xfa\xab\xdc\x48\xbc\x88\x77\x45\x59\x75\xd1\x07\xed\xe1\x8e\x8f\xe2\x1b\x78\x47\x42\x7c\x32\x97\x46\xdf\x7e\xf3\xee\xe3\x2f\x23\xf1\x5c\xa8\x0e\xc6\xeb\x20\xe6\x6d\xc2\x31\xa4\x60\xbe\x22\xc8\x55\x57\xd5\xdc\xaa\x0c\xfc\x59\xe6\xd6\x04\x70\xf0\x32\xd4\x8c\x89\x4d\xd7\x7f\x73\x15\x59\x56\xdd\xb7\xdf\xbc\x33\x50\x0c\x36\x7b\x49\xf8\xb8\xd4\xcb\x71\x26\xc7\xb5\xcb\x84\x0d\x54\x81\xc1\x88\x85\x75\xb7\x20\xee\xef\x3f\x53\xae\x88\xb1\x08\xa4\x63\xaa\xc0\xaf\xa2\x01\x82\x17\xbf\x1e\xe0\xca\x7e\xbf\x6e\x8e\x2c\xdd\xb2\xed\x2f\x73\x34\x2d\x40\x87\xdf\xa9\xb7\xf1\x13\x8a\x65\xe4\x22\x7b\xc0\x52\xff\x3e\xb8\x8c\x0c\x4b\xed\x15\x40\x75\x36\xc7\xc8\xce\xe6\x08\x5d\x4f\x03\xff\x2e\xf7\xbe\x7c\x5c\xe9\xdb\x4f\x37\xee\x2e\x41\x57\xd2\x6d\x38\xd9\xc5\x56\x05\x27\xd4\x4a\xc4\x8b\x56\xa2\x21\x88\xc6\xc9\xa0\x9e\xe9\x6b\x8e\x91\x4c\x24\xd1\x2b\x9c\x21\xbd\x44\xcc\x65\x1e\x77\xc5\x74\xa1\xec\x65\x0a\x78\x22\xfa\xd1\x7e\x99\xc3\x78\xae\x66\x84\x53\xb3\x36\xe4\xf9\x69\x07\xa6\xe5\xa9\x20\xc5\xa1\xad\x1d\xa1\x65\x39\x31\x22\xa6\xa0\xa5\x90\x9e\x21\xb7\xc2\xed\x21\x6f\xb8\x7e\x61\x53\x41\xd0\x24\x60\x3a\x9d\x5e\xfb\xed\xb5\xd6\x22\x98\xe7\xb5\xd7\x57\x5b\x51\x83\x3d\xbf\x49\x0f\x35\xe0\x41\x33\x8d\x86\xbd\x1c\x1d\x3b\xd7\x4f\xd2\xf2\x60\xc0\x75\x3d\x74\x87\xf9\xfa\x26\x82\xb6\x4f\xf9\xcb\xaf\xdc\xd9\xb7\xcd\xe2\x3a\x63\xd7\xaa\x81\xcd\xd2\x5f\x2f\x6d\x97\x1f\x72\x9a\x21\x1b\x69\x88\x19\xe3\x1b\x6c\x05\x79\xad\x2e\x1d\x08\x86\x0c\xc7\x06\xd8\xbe\xdc\x2e\x6a\x69\x4d\x1a\xd2\x21\xd6\x4a\x55\x68\x9b\x64\xbd\x08\xb8\x90\xe3\x8f\xf2\x43\x56\x11\xe3\x8a\x52\x56\xd6\xd5\x6b\x97\x71\x54\x3b\xb2\x60\xc5\x68\x7e\xcc\x48\x47\x84\x3a\x89\xdd\xe3\xf6\x27\x6e\xd2\xf1\xdc\x28\x61\x08\x43\x56\x78\x37\x3c\x9c\x3e\xae\xad\xcb\x81\xab\x32\xb3\xb8\x77\x97\xf8\x9e\x4a\x43\xd3\x6e\x70\x50\x12\xe6\xd0\x8c\x67\x5d\x1d\xfa\x7a\x24\x2a\xa2\x14\x73\x54\xe5\x00\xe1\x1f\xd3\x82\xb4\xed\xef\x7f\x48\xab\x22\xfe\xc9\x9c\x50\x1f\x6f\xc9\x75\x7e\xb4\x13\x1e\x79\xb8\xd7\xf3\xdb\x1a\x7b\x69\xfe\x47\xec\xb0\x7a\xd9\x99\x12\x23\xd7\x92\x59\x8c\x66\x58\xf2\x42\xed\xbb\x72\x70\x38\xfe\x6e\x59\xb0\x5d\x0d\x74\x00\xa1\xcd\x0c\x80\xf5\x36\xf6\xca\x94\xdb\x7e\x96\x3d\xe0\x1e\xe6\x03\xb1\x64\x33\xac\x9c\xdf\x0e\x9e\xc4\xee\x06\x0a\x68\x73\xe3\x26\x8a\xf1\xe0\x49\x8f\xad\xbf\x2a\xe4\xd5\xdd\xbe\x78\x5c\x77\x4d\x28\xb4\x3b\x7f\xbb\x07\x08\x5c\x2d\x73\x00\x85\x2a\xed\x55\xad\xbc\xf2\xb5\x03\x3f\xcb\x1e\xf0\xeb\x95\xd6\xd5\x0c\x87\x7e\x59\x3c\xb9\x94\x16\xe7\xc6\x4d\xf4\x76\xa5\xb5\x32\xdb\x21\xd5\xa2\x29\xea\x10\xc7\x65\x94\x7c\xa0\x8f\x6d\x8d\x09\xc6\xc7\xad\xc8\xf7\x59\x75\x85\x8a\x45\x79\x6a\x8e\xcd\xb8\xf1\xed\x0e\x3c\x1f\x67\xe8\x73\x97\xe3\x07\x06\x1e\xed\x97\x2f\xf1\xe4\x53\xee\x57\xf3\xae\x79\xf5\xd2\xd9\x2c\xf4\x49\x96\x51\x4b\xe6\x3e\x32\xe9\xaa\xe0\xda\x73\xa9\xee\xd3\xa7\x76\xcf\x38\x32\x01\x7a\x01\x35\x77\x53\xcb\x80\x37\x3e\x7e\xe0\xe3\xe1\x48\x35\xe6\xe3\xe2\xd6\x38\x00\xe5\x72\x8d\x32\x06\x02\x9c\xe6\x71\x92\xc6\x52\x13\x21\x0f\xde\x8c\xd6\xd7\xde\x2c\x76\x6f\xad\x84\xc7\x29\x58\xe3\xf8\xdb\x6f\xde\xfd\xda\xbb\x16\x5e\xc1\x6b\x11\x7c\xf5\x24\xf8\x58\xcf\x82\x15\x3f\xd6\xb1\x46\x40\x20\xac\xd3\x9c\xab\xee\x20\x1a\x8e\x1d\x98\x91\xee\x40\x49\xa3\x9d\x1e\xd2\xbf\xde\xd0\xc5\xed\x91\x7e\x9f\x7c\x61\xf7\xb9\xd7\x47\xf1\xc8\x7e\xf3\x60\x56\x92\x91\x53\x19\x4e\x2f\xc8\x7d\x7e\x12\x43\xf9\xa4\xf0\x9c\x5b\xad\x18\xda\xb0\xb3\x81\x96\x0e\xfb\xcb\x68\xb1\x6f\x97\xf9\x6e\xa6\x47\xb7\x94\x2a\x27\xa7\xb3\x5d\x96\x46\x06\x1a\x21\xbd\x1e\x5b\x7c\x25\x79\xd6\xb6\x54\xc0\x11\x18\x2c\x25\xa4\x79\xce\x13\x90\x79\x2a\xf2\x90\x78\x80\x76\xae\x89\xa3\x89\x93\x4d\xa1\x8f\x53\x1a\x73\xde\x4a\x6b\x8c\x7e\xe8\x4f\x7d\x08\x38\x2c\x86\x3b\x06\x3a\x69\x70\xfe\xcc\x75\xa4\xcd\x02\x34\x98\x19\x3d\x1b\xe7\x3d\xd6\x77\xcb\x89\x36\xd8\xd5\xe2\x24\x19\xe7\x46\xfe\x05\x39\x54\xdf\xc0\x15\xa3\x11\x8f\xc9\x38\x60\xa8\x55\x57\x92\xe7\xf8\x57\x3f\x9b\x2a\x7b\xf9\x29\x3f\x1f\x85\xb1\x50\x7b\x37\x86\x92\xc6\x1d\xd9\x6b\x89\xed\xd5\x41\xa6\xc1\xe9\xcb\xb2\xcc\xc4\x90\xba\x6d\x9e\xaa\xd6\x47\x88\x31\xae\x24\xa6\xd0\x15\x7d\xa8\x8f\x1f\x81\xbb\x21\x45\x28\xfb\xff\xc9\x60\xda\x35\x26\x9c\x97\xc6\xfa\xde\x14\xff\xc1\x9b\x0e\xcf\xb0\x62\x9f\xa1\xfe\xd8\x85\xd8\xe9\x55\xe1\xa1\x4b\x6d\x16\x09\xe1\x5c\xc7\x6b\x61\x4e\x39\xb3\xa7\xec\xc9\xd1\x3b\x00\x18\x7b\xec\x87\x1e\x47\x37\x62\x7a\x86\x72\x58\x02\xd5\x29\x18\xda\x81\xa9\x9c\x85\xa0\x2b\x85\xac\x68\xe5\x7a\x0a\x6b\x84\x5e\xf8\xc1\x3d\x2b\xea\x1d\x70\x40\xcf\xd3\x64\x57\xa4\x1f\xc6\xf6\xd5\x41\x3d\xaf\x24\x90\x38\xea\x70\x38\x2f\x50\x8c\xfa\xc1\x4e\xd7\x9a\xcd\x25\x43\x44\xcb\x5d\x20\xc0\x85\x18\x05\x35\x87\x9d\xc3\xd6\xdc\xdc\x0f\x52\x7e\x5e\x83\xf6\x08\x20\x50\x83\xe1\xed\xa4\xdb\x44\x73\x95\x6c\xf0\x41\xcc\x2f\x0c\xeb\x7a\x50\xe7\x45\x81\x19\x64\x0c\x46\xc8\xc6\xaf\x0b\x12\xf8\x93\xa0\x69\xde\x48\xc2\x60\x4d\x89\x58\xdf\x35\x9b\x68\x97\x7a\x8e\xf4\x3b\x4f\xee\x43\x3e\xda\x8e\xa4\x5f\x46\x2d\xcf\x00\x65\xb4\x8d\xbf\x28\xe2\xdb\xe9\xf7\x9a\x48\x14\x68\x84\x97\x3b\x19\xbf\xdf\xcc\xe6\xbd\xd1\xd4\xdd\x6a\xe1\x0c\xa1\x8f\x4d\x44\x77\x33\x86\x23\xa3\x3d\xd0\x08\xfe\x16\x06\xf0\x57\x37\x7e\x6f\x12\x45\xb0\x2c\xc2\x0c\x5e\x47\xf6\xb1\xb8\x01\xf0\xc4\xfe\xa8\x49\xe9\xb9\xbe\xab\x81\x83\xdc\xe7\xce\xd5\x17\xf7\x87\x91\x01\x84\x9d\x09\x7a\xe3\x19\x06\x7e\x54\xdf\xb3\xd4\x83\x0f\x5b\xac\xf0\xe7\xe5\xe5\x6d\x12\xaf\x73\x7c\xd5\xd5\x03\xad\x33\xb4\x01\x12\xfc\x80\x83\xb8\x83\x00\xee\xc4\xe8\xf3\x92\x36\xca\x7b\x48\xed\x1e\xb6\x79\x98\x71\x85\x1e\x66\x14\x5f\xe5\xfa\x28\x7e\xd9\xf1\x7b\xb8\x85\x76\xe5\x58\x15\xb7\x69\x53\x15\x05\x5b\x25\xb3\xa7\x11\xcc\xab\x23\xce\x45\xc5\xd8\xb3\x5e\x09\x3f\xd0\x38\x5f\xad\x26\xd1\xf0\x9f\xe9\xcc\xfb\x0a\x99\x13\xc9\x21\x17\x75\x83\x5d\x36\xe7\xf5\x5a\xf3\x6d\x49\xd9\x7a\xcb\xc9\xba\x48\xe3\x3d\x61\x89\x1c\xb1\xd4\xee\x9f\x28\xce\xf5\x4a\xf5\x71\x17\xfd\x53\x7e\xae\xab\xa6\x23\x5c\xd4\xda\x26\x96\x59\x66\xbe\x1d\xcf\x59\x1c\x96\xc7\xa2\xf3\x01\x9a\x8b\x23\x21\x4b\x4d\x98\x02\xdb\x40\xd1\xef\x02\x19\x74\xcc\x2b\x43\x5d\x55\xdb\x30\xd2\x00\xf6\x1f\xf9\xa3\x57\x28\x1c\x67\x08\x3b\xc3\x81\x3f\x59\xa4\x29\xc9\x9b\xb9\x54\x21\xb1\x17\xf0\x06\x71\x82\xdc\x58\x22\x2f\x71\x46\x9f\xf3\x94\x4a\x25\x5b\x3e\x24\xf5\xcb\x47\x52\x66\xd1\x87\xaa\xc9\x69\xd9\xf1\xe8\x4c\x41\xca\xac\x4d\x49\x4d\x75\xfd\xbb\x07\xa3\xef\x84\xe3\x30\xb0\x3a\x4f\x12\xfd\xbd\x97\xde\xde\x93\xbc\xa4\x4d\x7c\x28\x2e\x79\xf6\x84\xd4\xe4\x02\xe1\x16\x8b\xcd\xe0\x0a\xc4\x8b\xff\x84\xd8\xba\x7b\x3f\x2b\x74\x97\xf6\xbc\xa5\x41\x98\x03\x85\x3e\x61\xa5\xa9\x25\x50\xf7\x5f\xb0\x1b\xfa\xe6\x09\x68\x66\x19\xaf\x33\x68\x46\x15\xc8\xae\xae\x77\xb0\x60\x2a\xf7\x0b\x7e\x57\xd2\xbe\xcb\x68\xa6\x1d\x58\x24\xd7\xf1\x1e\xc2\x48\x78\x83\x24\x86\x75\x0d\x12\x15\x2f\x3a\x20\x7f\x31\x2f\x7f\x62\xe1\x6e\x9d\xea\xcc\x8a\xae\xf5\x54\xf7\x4d\x6f\x10\x90\xe8\xa0\xe9\xca\x0c\x71\xeb\x95\x88\x5b\x87\x1c\xc9\x9a\x27\x56\x3b\x58\x8d\x70\x81\x39\x7c\x1c\x89\x73\x63\x84\x54\xd4\x14\x75\x12\xc3\xba\xf7\x69\x18\x51\x3a\x43\x8a\xbf\x27\x73\xd0\xea\x70\xc0\xd4\xd9\x66\xc3\x52\x02\x78\x15\xc4\x77\x33\x92\xa9\xb1\x26\xfa\x2d\xd8\x87\x87\x2e\xee\x83\xf6\xc9\x70\xc5\x0c\xcd\x90\x3b\xea\xb7\xdf\x22\x7f\xc3\xeb\x4f\x9a\x04\xbc\x39\x46\x74\x59\x4d\xf3\xb4\x2a\x63\xe9\xef\x3a\x53\x2f\xcd\xe7\xfa\x5b\xf6\xf8\xf9\x04\x7b\x68\x99\xb5\x7c\xd2\xeb\x83\xa2\x5e\x5e\x6b\xf6\x40\x6f\x9b\xab\x1e\x4b\x33\xe4\x76\x96\xdc\x58\xd8\x4c\xfb\xb1\x16\x1b\x9b\x44\x02\x12\x6c\x3d\x69\x2f\xb8\xe9\xe3\xd5\x7a\xed\xd4\x35\x3c\x35\xc7\x41\x35\x67\x63\x36\x47\x6d\xb2\xa0\xd7\x9d\x07\x7d\xe6\xd6\xfe\x11\xac\x26\x94\x33\x6a\xfa\xb2\xf6\x43\x93\xa3\xca\xa9\x74\xf0\xed\xbe\xaf\xb3\x41\x66\xba\x80\xc1\x58\xb9\x50\x58\x8f\x0c\xaf\x49\x6a\xa7\x67\xe4\x3d\xa4\xb9\xb2\x09\xa3\x1c\x80\x00\x01\xd2\x69\xe1\x24\x4c\x5b\xeb\x07\x96\xb1\x3d\xde\x08\x57\x0e\xcf\x6b\x57\x97\x52\xb3\xcd\x59\x46\xa9\x7a\x62\xb7\x48\x85\xae\xac\xe5\xe8\x3b\x74\x18\x18\xe3\x40\x98\x3f\x7b\x20\xe0\x56\xf9\x50\x35\x67\xec\x50\xd2\x2a\xd0\xdc\x9a\xae\xa3\x61\x6f\xed\x99\x21\x78\x51\xeb\x8c\x0e\xdd\x6b\xf1\x3b\x91\x00\x2c\xd9\xc0\x1d\x17\xc8\x21\x84\xaf\x74\xc4\xfa\x5e\xfa\x75\xdf\x5b\xb6\x6b\xfa\x55\xde\x5b\x76\x56\x13\xfe\xde\xb2\x46\xe2\x6e\xef\x2d\x3b\xa9\x9a\xa7\x52\xdd\x80\x8e\xf7\x96\xc3\x10\x7c\xef\x2d\xbb\x28\x04\xbe\xb7\xac\xa1\xdf\xe7\xbd\x65\x9d\xe4\xf0\x02\xae\xf6\xfd\xb7\x7b\x6f\x19\x65\x47\xbd\xb7\x8c\x30\x35\xfe\xde\x32\x4e\xd2\x71\xca\x12\xa9\xe1\x4e\xef\x2d\x6b\x94\x6f\x7f\x6f\x39\xd0\xcd\xc1\xed\x8c\xbd\xe5\xe3\x1e\xcd\xe6\x75\xbb\xd1\x4d\x94\x6b\x2c\x20\x1a\x13\xd4\xa6\xbf\xc4\x13\x87\x1b\x8f\x10\xdc\xc7\xc7\xc2\x3c\xd6\x91\x60\xbd\x19\x8c\xbf\x3d\x52\x8f\x07\xb0\x46\xb9\xc0\x16\xd0\x16\x23\x4b\x6b\x47\xe1\xd7\xbb\xbb\x2b\xd7\x99\xfa\x49\x42\xc4\x0f\x01\x0e\x87\x8d\x3b\x05\x19\x79\x21\x89\x19\xbe\x51\x31\x43\x96\xed\x82\x88\xc8\xb0\xab\x11\x59\xe2\x44\x90\x85\x1f\x7d\xe9\x6c\x74\xdc\x71\xba\x22\xe8\x66\x52\x77\x78\x99\xe6\xca\x58\x57\x7a\xc3\x2f\x0c\xac\x73\xc8\xd2\x04\xea\x74\xc7\xb5\x87\xfc\x66\xda\x2a\xdf\x8c\x9f\x23\xae\x25\x4a\xe8\x7f\x23\x74\x8d\xd1\x8d\x0c\x45\xf8\xc2\x2d\x76\x62\xe9\xa1\xff\x87\x9c\x90\xa3\x9b\xfe\x9f\x83\x98\x1d\x50\xc2\x8f\x17\x3a\x71\xcc\x65\x0a\x0e\x84\x9e\xc7\x62\x07\xea\xae\x39\x49\x88\xd1\x57\xda\x13\xce\xb7\xb6\xfc\xb8\x1e\x6d\xac\xc5\xfa\xf9\xcf\xd0\xf7\x7b\xc3\x5b\xdc\xd3\x37\xf6\x88\x47\xc1\xf4\x6d\xf3\x71\x70\xfb\x90\xa8\x38\x3d\x87\x9d\x8b\x0c\xd1\x2f\x46\x5e\xcb\xf0\x18\x06\x18\xc8\xb8\xf7\x7c\xab\xbc\x50\xf3\x16\x2d\xb3\x12\xf5\xa8\xa7\x07\xb4\xd3\x8b\x6e\xdc\x31\xad\x11\x50\x63\xaf\x25\x04\xd6\x66\x84\xde\x10\x4a\x0f\x0f\x0f\x23\x94\xec\x5d\x23\x13\x42\x39\x35\xb7\x58\x1c\xd6\x6f\xf0\x64\xf0\x08\x50\xa0\x26\x58\x27\x89\xaf\x54\xe0\x40\x4f\x13\xa9\x7b\x2c\x1a\xa4\x59\x1a\x6d\x56\xb8\x8a\x98\x71\xec\xe3\x7a\x7c\x70\x1c\xc4\xb0\x4a\x63\x43\xe4\x36\xa6\x07\x5b\x75\x13\xcf\xae\xf3\x2e\x37\x12\x41\x5a\x2f\x34\xc3\xa7\x1a\x37\xb6\x1c\x18\xbc\xdb\xd8\xc6\x0c\xe1\xdb\xc8\x20\xed\x17\xf6\x31\xa4\xf7\xbd\x23\x5a\xa5\x8d\x0a\x9f\x53\x1d\x8f\x67\x80\x77\xb7\x4d\x44\x2d\x3f\x55\x40\x45\xd6\x53\x24\x4e\x80\x9b\xd8\x40\x1e\x1a\x09\x00\xf5\xbe\xfc\x33\xd6\x82\xab\x70\x30\x1f\x04\xbc\x8c\x2e\xf0\xf2\xf2\x99\x36\xe2\x88\x04\xf6\xe0\xf7\x7c\x8e\xf8\x95\xc9\x43\xff\xcf\x41\xc9\xed\x57\x6e\xb3\xfe\x5f\x08\x9a\x29\x51\x1c\xe8\xaa\x53\xad\xee\x19\xdf\x24\x6e\xf9\x95\x41\x7c\xa3\xae\xe5\x55\x98\x63\x8d\xf6\x7a\x97\x77\x68\xb7\xc3\xbb\xf4\x82\xd9\x53\xb3\x1f\xdc\x1e\x69\xee\x63\xc8\x41\x5a\xe6\xf4\x2e\x47\x00\x03\x19\xf7\x7a\x97\x4b\xf1\x2e\xf5\x5b\x74\xcd\xe9\x5d\xda\x16\x08\xc7\x1d\xd3\x9a\x30\xef\x32\xb4\xb6\x71\xef\x12\xbc\xb9\xe5\xa0\x64\x7b\x97\x26\x84\xcb\xbb\x9c\x25\xfd\xbf\x10\x8d\x30\xbd\x4b\x0f\x50\xa0\x26\x38\xbd\xcb\x40\x05\x0e\xf4\x2e\x91\xba\x5d\x33\x3b\x92\x1c\xdd\x65\xa8\x35\x27\x26\xb4\x0a\xfd\x99\x07\x5f\x03\x6f\x22\x8f\xf8\xc7\xd2\x5c\xde\x4e\x0f\xf1\x95\xae\xc3\x47\x9c\x24\xd1\xc1\x57\xb9\xc8\xe1\xdd\x88\xba\xc8\xd7\xa3\xbf\xa5\xe1\x5e\x17\xd9\xdd\xfa\x37\x76\xbf\xcb\x45\xbe\x85\xc0\x9b\x5a\xef\x77\x91\x85\x91\xbf\xda\x45\x36\xeb\xb7\x3c\xd7\x20\xdf\xc0\xe1\x9e\x7a\x8c\x2c\xea\x25\xfb\xeb\x72\x39\xca\x36\xc0\x4d\x9c\xb8\x1d\x65\x1f\x68\x88\xa3\xec\x6c\xc1\x55\x38\x98\x3b\xb5\x5c\x2e\x65\xb3\xf6\x0d\x25\x59\xda\x5c\xce\x7b\xfd\xb4\xc1\x83\x7d\xd8\xc0\xbc\x36\x10\xfa\x4c\x11\x7b\xd0\xc5\x7f\x10\x6b\xe0\x42\x9e\xb2\x70\xec\x34\x23\xe0\x9f\x8a\x7c\xb7\xa7\x87\xaa\xa1\x7a\x0b\x12\x79\x07\xca\x58\x0e\x0e\x6f\x40\x7c\xfe\x4b\x92\x90\xe4\x3d\x42\x15\x5e\xf7\x40\xd6\x62\x35\x39\xe6\x25\x3b\x09\xe8\xe6\xd5\xbe\x76\xa0\xbf\x0a\x95\x18\x59\x80\x11\xa9\x0c\xd5\x38\xa4\x82\x02\x32\xc7\x40\xff\xd2\xd6\x24\xf0\xe9\x83\x47\x57\x1e\x21\x2b\xe7\x81\x75\x78\xcb\xf5\xcc\x90\x7c\x27\xe7\xaa\x47\x80\x7c\xb7\x94\xd1\x56\x6b\x99\x21\x2c\x09\x68\xa5\x4a\x1a\xf6\x26\x63\xd8\x0e\xdb\x48\x0e\x59\xbd\x66\x90\x48\xc2\x66\x0b\x14\x2a\xae\xde\xb2\x8d\xe7\xe2\x02\x2c\xbf\xcc\xef\xf2\x6d\x67\x5b\x63\x80\xeb\x88\x14\xfa\x1e\x3c\x0d\x4e\x72\x80\xf7\x26\x5c\xa6\x61\x9f\xb1\xb6\x18\xb3\xbc\xc9\xb5\x2c\xee\x59\xc7\x11\x41\x8b\xdd\xe5\xd0\x98\xea\x69\x2d\x0d\xcd\x0d\xbb\x8c\x6f\xbd\xc6\x8b\xdf\x51\x85\xdc\x68\x8b\x41\xbc\x00\x6d\x85\xe9\x06\x58\x02\x52\x00\x88\x88\xb4\x32\x5c\x2d\x0c\x18\x67\x32\x8e\xe0\x9c\x1b\xba\x0d\xf0\x6b\x4c\x5c\x1c\x6d\xc3\x27\x3e\x0e\xb6\x2f\x24\xbb\x97\x8b\xb6\xc7\xbc\x20\x00\xd8\x58\xd6\xac\xc5\x7a\xd4\x9e\xac\x3d\xbc\x38\x4d\x8a\x5d\x3e\x6e\x55\x50\x56\x2c\x10\x84\x97\xf6\x8c\xc8\x9c\x7f\xb4\x65\xee\x49\xd9\xe6\x22\xed\x13\xb9\x0d\x30\x2a\xf2\xc5\xa8\xc8\x17\x1e\x5e\xdc\x22\xb7\xca\xc7\x45\x8e\xb2\x62\x81\x00\x5e\xc4\x50\x0a\x72\x27\xf0\x44\x7d\xae\x54\x43\x9c\xf8\xa8\x77\xc1\x60\x64\xcb\xf9\x1f\xaa\x99\x63\xde\x8f\xe8\xfc\xa5\xeb\xa0\x7f\x70\x4a\x12\x75\x60\x7e\x65\x0a\xc7\x9c\xe0\xe4\xa7\x37\x27\x2a\xe2\xb4\xa6\x25\x7d\xe9\x40\xeb\xf9\xdf\x4a\x00\xf0\xe4\x84\x81\x58\x37\xf4\x39\xaf\x2e\x2d\x44\x56\xdf\x4c\x02\x30\xef\x82\x80\x35\xad\xbd\xfe\xcd\x68\xb2\xd3\xc6\x6b\x65\xaa\xd6\xb7\x18\x66\xc9\xe6\x70\x5c\xcf\xd0\x02\xad\xff\xa7\x73\x7a\x8e\xa6\xeb\xfe\x3f\x0b\x7a\x36\x4c\xc0\x66\xf5\xbb\x47\x33\x29\xe5\x66\x2c\x29\x25\x7c\xb0\xd1\xd2\xf5\xc0\x94\x9a\x7b\xd2\x52\xf5\x06\xbb\xae\x61\xd3\xf9\x8a\x9e\x45\x1b\x09\x6f\xa4\x94\xb5\xfc\xd3\x19\x29\x1b\xcd\x36\x25\xd2\x81\x6b\x22\xdc\xd1\x73\xdd\xbd\xea\x82\xb4\xde\x1e\x19\x84\x8d\xbb\xf1\xea\x7a\xb9\x46\x7a\xec\x78\x0d\x58\xd6\x68\xf0\x3f\x9e\x1a\x7a\x18\xd6\xb4\x58\x99\x37\xab\x15\x3f\x05\xa3\x93\xae\x9b\xfc\x4c\x9a\x57\x17\x8a\xee\xf5\x68\x28\x28\x37\x7a\x99\x97\x9b\xf9\xc3\x3a\x19\xde\x1e\xe4\xe8\xed\x25\x4d\x69\xdb\x3a\x1b\x90\xee\x1f\x56\x29\x8a\x82\x72\xa3\x97\x79\xb9\x59\x2e\xb7\xd9\xb0\x04\xe7\xe8\x79\x79\xa8\x9c\xac\xec\xd3\x24\xa3\x36\x3c\xca\x07\x28\xf0\x32\xb1\x98\xed\x93\x6c\xa5\x13\xfd\x4a\x9a\x52\xbe\x13\x8e\x8d\xfc\x84\x64\x4b\x8a\xa2\xa0\xac\xe8\x65\xfe\x24\x68\xe9\x76\x33\x3b\x18\x9a\x48\xca\xa3\x1b\x23\xdb\xae\x16\x4b\x14\x03\x57\x5d\x58\xe4\x65\x25\xdd\x2e\x92\xb9\xea\xf8\x3d\xc9\x8e\xd4\x3f\xd1\xc1\xf7\xa0\xcd\xeb\x89\x8b\xfa\x25\xda\x38\x53\xd5\xfe\x9d\xad\x1e\x6a\x0d\xb0\x19\x17\x9c\xbf\x64\xf2\x08\xb2\x57\x83\xe4\x02\xed\xd5\xf0\xec\xc4\x4b\xfb\x04\x68\x88\x67\x32\x5e\x5a\x48\x73\x38\x9f\x3b\x38\xf6\xda\x3b\x48\x44\xb0\xaa\x0c\x37\xff\xf3\x7e\x86\xbb\x77\xa9\x38\xbb\x79\x47\xcf\x72\x9d\xa8\x58\x1e\x72\x15\xa9\xb5\xe9\x13\xe0\x1f\x59\x03\xfa\xa7\x5a\xbd\x3a\x48\x0a\x73\x40\x70\xf0\x4f\x00\x4b\x3f\x98\xb9\xc2\x53\x33\xe9\x3c\x6b\x01\x13\xe0\xa2\xfe\xf5\x72\xde\x57\x5d\x63\xe6\xa1\x5e\x20\x37\x96\x64\x10\x51\x26\x6e\x17\x2d\xcd\xcb\x13\x6d\x72\xd7\x3a\x19\x78\x64\x43\x55\xd3\xd3\x6c\x12\x81\xbf\x4f\x33\x28\x57\x41\xd0\x46\xab\xb1\xe3\xd5\xc8\xfd\xe1\xf9\x0c\x19\xa2\xf3\x24\xb1\x28\x3e\x9d\x1a\xd3\xdd\x57\x26\x6a\xd5\xff\xb3\x72\x0b\x00\xae\xed\xfb\xf7\x91\x21\x4d\x77\xae\x69\x20\x8a\x81\xf4\x2f\xce\xd7\xb6\xc4\x16\x5d\x9b\x36\x94\x96\x11\xcb\xbb\x30\x18\x2e\x78\x94\x58\xaf\x7f\xe8\xce\xe5\x83\xb8\x3b\xf5\xef\x5a\x66\x81\xab\x5a\x63\xe6\xa9\x90\xcf\xe7\x1b\x6b\x9b\x35\xb8\x53\x38\xda\xe3\xb0\xd7\xd6\x0b\xfd\x36\x5d\x77\xba\x9c\xf7\x25\xc9\x0d\x27\xd5\x5e\xa3\xe0\x67\xc6\xe7\xd8\x2d\x55\x23\xb3\xe4\xdb\x17\x34\xc6\x43\xf9\x6c\xe7\x45\xd8\x4b\x0e\x19\x4d\xe7\x6d\x44\x49\x4b\xe3\xbc\x8c\xab\x0b\xbf\x5e\x57\x05\x02\x8e\x43\xd9\xc2\x62\xd9\x3f\x27\xd1\xf0\x05\x64\x03\x85\x56\x43\x5e\xf6\xd0\x0c\x03\x48\x2e\x43\x06\x0a\xea\xe1\x5a\xf0\x6d\xb0\xcd\xc3\x27\x67\x62\x4d\xdd\x3b\x1c\x78\x9d\xa6\xa4\x56\xa1\x78\x78\x37\xfd\x11\x3f\xf1\x44\x0a\xda\x74\x46\x44\xc8\xbf\xd1\xf1\x86\x1b\xe6\xbc\xb2\xd3\x12\xbf\x59\x82\xdb\x2a\x8e\xc3\xff\x67\xd8\xec\x32\x5d\x05\x0d\xfa\xa9\x9e\x08\x84\xa7\x4b\x81\xde\x1f\x31\xc0\x3f\xd5\x16\x47\x2b\x83\xeb\x38\xcb\xdb\x73\xde\xb2\x65\xa3\xa4\x2e\xbf\xb1\x6c\x39\xbf\xd8\xf9\x96\x16\x3e\x22\xd1\x34\x2d\xaa\x16\xa7\xc5\x8b\x46\x9d\x05\xe1\x36\xc9\x8b\x08\xd2\x46\x7b\xe4\xa8\x79\xf9\x4a\x1b\xd2\xcd\x7a\xe1\x5a\xde\x66\x87\x43\x92\x61\xf7\x0d\xb2\x35\xdd\xa6\x6b\x9c\xba\x7b\x0e\x48\xb7\x74\xbe\x5f\xe0\x58\x66\x1f\xab\xd5\xca\x7e\xb5\x1c\x3c\x50\x0e\xa4\xd6\x07\x83\xff\xbe\x49\x1e\x5c\x87\x33\xb2\x2d\xcd\x0e\x58\x64\x79\x9f\xd2\x87\xc3\x0c\x21\xed\x6e\x01\x59\xd3\x19\xc5\xb8\x71\xb2\xbf\x5c\xcd\xd7\x5b\x1d\x01\xae\x2c\xd4\x51\x6d\xb2\xce\x16\x7b\x97\x11\x4d\x0f\x0f\x74\x81\xb4\xe0\x40\xe8\x3e\x4d\x71\xea\xee\x46\x1c\x36\x74\xb6\x5f\xe1\x58\xae\x76\xac\xd7\xab\x99\xd9\x0d\x60\x4d\xa2\xe4\xb3\x5d\x2e\x97\x73\x57\x33\xe6\x19\xcd\xb0\xad\x8f\xbe\x11\xd9\x0c\x25\xee\x6e\x05\x5d\xee\xb7\x69\x82\x22\xb9\x1a\xf1\xb0\x5c\xac\x16\x72\xad\xf9\xcf\xdf\x7e\x23\x67\x99\x2f\xf4\xf5\xd0\x90\x33\x6d\xa3\xba\xa9\x8e\x0d\x6d\xdb\x78\xcf\xd2\xe2\x34\x79\x4d\xf9\x70\x39\x34\xd5\x39\xfa\x05\x34\x6a\x18\x9a\xcb\x84\xfb\x02\x8c\x6a\x67\x2d\x5c\x07\xc0\x21\xc5\xcb\xbf\xf3\xea\xab\xbf\x57\xcd\x7f\x8f\x6a\xa7\xb2\x2a\x06\x0f\x33\x26\x78\xa6\x9b\xc0\xb4\xdd\xbe\x7d\xf5\xc7\x80\xfb\xf7\x73\xe4\x3d\x5d\xef\xbd\x7a\x14\x41\x05\x2c\x81\x4c\xcd\xa0\xe6\xe3\x90\x12\x85\x4d\x7b\xca\x91\x12\x09\xa2\xc7\x5e\xa3\x9a\xeb\x6b\x05\xff\x3a\xd8\xbb\xcd\xe6\x93\x49\x0c\x72\x07\x80\x16\xba\x1f\x25\xf6\x22\x60\xce\x1c\x3f\x17\x37\x5d\x73\xe7\x0b\x71\xe2\x6c\x00\x77\x29\x26\x78\xa1\xcc\xd9\x24\x1a\x3e\x8b\x4f\x91\xdd\x43\x76\x32\x0d\xc9\x74\x2f\x7d\xd2\xc4\xc7\x5e\xa3\x68\xd9\x7d\x58\xae\x32\x7a\x9c\x38\xd2\x2a\xac\x3e\xf6\x3e\xf8\x7c\xf5\xbb\x09\x74\x8b\x22\xeb\xc3\x2a\xf9\x9d\x9b\x04\x2b\x75\xa7\x65\x58\x7d\x8c\x36\x26\x3d\xf3\xc3\xc7\x47\xbc\x4d\xd5\xb5\xcd\x61\xac\xb3\xdb\xda\xff\x80\xcd\xf9\xbf\xb7\x2d\x6a\xec\x0d\x6d\xe2\x63\x9e\x99\xd6\x65\x62\xed\x0f\x19\xa5\x98\xc2\xab\x45\x85\xfa\x2a\x4f\xee\xd8\xea\x2e\xeb\x27\x65\x7e\x16\x11\x1e\x74\x22\x98\xb7\x42\xc8\x51\x5e\x1e\xf2\x32\xef\xe4\x48\xbd\x0d\xf1\x7a\x2c\x7c\x64\x5f\x13\xac\xf6\x0f\x7e\x17\xad\xff\x34\x02\xff\x98\x03\xe7\x3f\x8a\x11\x40\xf5\x3a\x7c\xdb\x63\x44\xa9\x31\x42\xff\xa9\xd1\xff\x78\x5a\xf0\x1f\x5e\xa3\xaf\xda\x43\x1b\x51\x6a\x07\xad\xff\xd4\xeb\x7f\x3c\x5d\xf8\x0f\xaf\xd7\xd7\xec\xc6\x8e\xa8\x35\x4e\xea\x3f\xb5\xfa\x1f\x4f\x13\xfe\x23\x6a\x35\xdf\x07\x33\xc3\xdf\xf0\x54\x19\x83\xb0\x1e\xec\x44\x1f\x62\x65\xa0\x93\x88\xff\x6f\xbc\xaf\x32\xbe\x2b\x8e\xc5\x70\x7e\xae\xd8\x56\xa3\x86\x39\x60\x0c\x1b\x76\x49\x62\x70\x12\x57\xfb\xbf\xd2\xb4\x43\xb6\xb0\x74\x30\x16\x16\x97\xbc\x3c\x4d\xeb\x4b\x51\x80\x4c\x3c\xc6\x1b\x08\x56\x25\xfd\x77\x03\x59\x65\x13\x32\x9f\x55\xb0\x90\xfb\x66\x28\x29\x40\x4a\x80\x01\xc7\x93\x2f\xe6\x09\x85\xae\xaa\x75\xda\x3c\xa5\x1c\x23\xe1\x7f\x16\x59\xb2\xa2\x52\x52\x5b\x87\x1f\x58\x91\x0e\x7e\xa2\x24\x93\x53\xac\xb5\x41\x83\x65\x58\xd3\x64\x96\xb7\x98\x70\xbd\xaf\x3b\x0e\x3b\xf3\xfe\x43\x9d\x5a\x5c\xd0\xb1\xad\xef\xd9\x30\x09\x79\xea\x11\xa9\x4c\x3d\x4f\x72\xe3\xa5\x0c\x83\x41\xd7\xab\xb7\xb7\xe4\x30\x73\x55\x61\x24\xb5\xf3\x64\x4e\x73\xdd\x99\x18\x39\x18\xbc\x04\x67\x4a\xb0\x0e\x00\xf9\x36\x9c\x70\xe6\x07\x4d\xef\xec\x5d\x4a\x8b\x00\x38\x7f\x68\x14\x38\x33\xc2\x5c\x75\xe1\x86\x05\x93\x5d\x47\x5b\xe4\xe1\xd1\x89\xd5\x0c\x55\x04\x6e\x05\x38\x41\xee\x70\x29\x00\x9c\x04\x71\x55\xe3\x14\xf5\x28\xf7\x6e\xcc\x80\x66\x85\x74\xb0\xb1\x41\x19\xde\x82\xbe\x2b\x6f\x60\x9f\xa1\xdd\xc2\xbb\x2f\xf9\x18\x7e\xee\x09\xe1\x8e\x17\x0c\xc7\xf0\xf0\xe2\xe0\x2b\x35\xb7\xdf\x9f\xc1\x6b\xbe\x46\x4f\x46\x30\x9e\xa6\xed\x99\x14\x05\x2a\xea\x31\xd4\x31\xcc\x5b\x54\x33\x08\x73\x9c\xe9\x31\x02\xa3\xf8\xfe\x91\x71\x3b\x66\x00\xeb\x23\x04\x18\xfe\x15\x43\xd3\xd5\x8b\x8e\x81\xe9\x97\x9f\x73\x58\xfa\x79\xb7\x06\x65\xba\xc9\x32\xea\x3a\x20\x78\xeb\xc1\x07\xd7\x04\xe4\xa1\x37\x8a\x72\xbd\x6d\x74\xd2\x72\xce\x83\x0a\x00\x49\x77\xe4\x6d\x75\x02\x4e\x75\x38\xa9\x82\xd3\x4b\x7e\x88\x61\x1e\x1c\x85\xbc\x26\xd9\x06\x68\x82\x61\xe9\x60\x0f\x58\x35\xde\x72\x68\xc4\x29\x0a\x07\x31\x3f\xfc\x3d\xba\xbe\x27\xe4\xee\x77\x56\x8a\x74\xba\xaf\xa5\xe9\x92\x2e\x0e\x4e\x5f\x8b\x91\xf4\xf4\x38\x28\xf6\xb3\x85\xcd\x71\x01\x7d\x3d\x70\x6e\xf4\x35\x94\xb7\x55\xe1\x8d\x27\x6c\x9c\x42\x70\xd3\x1b\x45\xb9\x47\xa7\x0b\x5a\x6e\x01\x4b\x00\xbb\xeb\xfd\xad\x26\x87\xb9\x3a\x3e\xe4\xa4\xea\xe9\x7d\x1d\xc2\x37\xde\x0d\xc8\x6b\xc6\x3b\x68\x82\xae\x03\x5a\x0f\x58\x35\xde\x76\x3a\xc9\x29\x0c\x27\xb9\x31\x8c\x7b\x28\x00\x27\xe5\xee\x7f\x51\x6e\x4b\xd5\xdb\x62\xba\x4f\x53\x4f\xf7\x73\xa2\x9e\xde\xd7\x00\x7c\x9d\xaf\x03\x5e\xd3\xf7\x80\x7f\xbd\xef\x35\xd9\x7b\x45\x7c\x45\x20\xc3\xe9\x65\x60\xeb\x68\xe3\x1c\xf4\x62\xb8\xae\x58\x52\xf4\xdc\xe9\xdc\xde\xd6\x1f\x0d\x27\x8c\x1e\xae\x75\x9d\xe4\xe1\xc7\x94\x66\xf6\x31\xa5\xc4\x3e\xc4\xe3\x83\xd5\x5a\x35\x44\xe8\xb4\xe3\xc2\x3a\x0c\x94\x3d\x1e\x66\x09\x7d\x35\x25\xec\xfa\x32\x7a\xa7\x18\x65\x09\x7d\xd9\x19\xe4\x65\xc3\xdd\x5f\x4e\xa2\xcb\xbb\x82\x06\xe9\x53\x62\x1e\xe3\x5a\xfb\x0e\xe6\x02\xea\xd2\x93\x8f\x90\x8f\xe2\xd2\xeb\xf0\xd9\xfc\x7b\x58\x04\x58\x5f\xf5\x4c\x85\x68\xfd\x87\xaa\xea\xf4\x8b\xd5\x66\x97\x05\x9c\xb9\x33\x9e\xca\x31\x0e\xf8\xbb\xae\x76\x8f\xc4\x9b\xcc\xbe\x7c\x02\x83\x54\x8a\xe0\x49\xb4\x42\xa6\x9b\x7b\x32\xe3\x8a\xae\x03\xe0\x16\x45\xcb\x58\x07\x55\x61\x22\xc9\xe4\x7b\xf6\x1b\x7f\xc8\x18\x76\x33\xa3\xc5\x0b\x7d\xc1\xc4\x10\x26\x83\x89\xe9\xcc\x8f\xbf\xfc\x70\xed\x60\xd4\x98\x02\xe1\x4a\x4f\x28\x33\xa8\x7d\x61\xa4\xf4\xd6\x05\xc7\x45\xaf\xd7\x53\x69\x73\x3e\xf9\x14\xc5\x17\x11\x96\x87\x44\xed\xb9\xe9\x93\x3d\x68\x3d\x78\x0e\xc9\x75\xe2\x2a\x83\x28\xc6\xfe\x8c\x1b\xda\xd6\x55\xd9\xb2\x9b\x7c\xec\x8b\x7a\xe0\xd6\x3b\x9c\xd0\xaa\x22\x71\x33\xc5\xa8\xc3\xf1\xd9\xae\x5a\x02\x0a\x16\x82\xdf\xb6\x36\x78\x33\xa9\x6b\x79\x33\x78\x29\x3e\xb2\xac\x22\x6b\x9c\xbc\x75\x58\x5c\xc9\xda\x53\xd7\x4f\xc6\xfa\x97\x46\xe3\x5d\x4d\x06\x37\xd0\xee\xb5\xf7\x1a\xda\xd7\x71\xe6\x96\xea\x68\xcd\xff\xf0\x62\x8f\xba\xec\x6e\xbd\x30\x5a\xd5\xe9\xb7\xea\xf0\x7b\xb6\x6a\xb4\x2a\x7f\xab\xee\xda\x1b\x77\x95\xf7\x5d\x25\xfa\x16\x99\x8d\x0f\x92\xdf\x60\x10\x80\xf9\xfb\x57\x1e\x03\x77\xaa\x29\xa0\xc3\x7e\xab\x9a\xbc\x6d\xba\x67\x4f\xdc\x53\xd6\xf7\x94\xe6\x1b\xe4\x65\x2b\xff\xd5\x53\x00\xc8\x60\x25\xd8\xc1\x9c\x51\xb3\x04\xf3\x2d\xef\xb0\xde\xb9\x8a\x3b\xd1\x87\xf0\x43\x13\xaa\xb5\x7e\xba\xbd\xb7\x19\x4e\xf7\x0a\x96\x9c\xf2\x1c\xa9\xf3\xff\x02\x71\x5f\x33\x6b\xbe\xa9\x9a\x70\x3f\xe0\x0d\x9d\x7c\xbf\xd6\x8c\x54\x33\x32\xcb\xde\x4d\xfc\xf7\x93\xf0\xfd\x84\x78\xb3\x9c\xd0\xe1\xf0\x1b\xeb\xfa\x5d\x0c\xcd\x68\x47\xfc\xfa\xe6\xec\x6e\x6d\x19\xed\xc0\xbb\x98\xd0\xb1\xb9\xf4\x5e\xd2\xbd\x9b\x00\x6f\x95\x51\x14\x60\xf0\xd1\x60\x44\xdf\xea\x4f\x66\x90\xc3\x2a\x02\xfd\x6a\xcc\x4a\x9f\x00\xb0\x4f\x1d\x3e\x99\x71\x72\x77\x68\x14\x1b\x8a\x21\xde\x9c\xc1\x59\x88\xd3\x86\x85\xf4\x50\x4b\xc0\x21\xa8\x59\x87\x1d\x8d\x51\x90\xa0\x91\x63\x44\x39\xa7\x4f\x5d\xf3\x64\x98\xc0\x68\x14\x7e\x64\x7a\x1b\x30\x7a\x5d\xba\xa6\x06\x05\x1f\x5c\x43\xef\x8a\x5e\x53\x83\x82\x0f\x9e\xa2\x83\xe5\x76\x1b\x8d\x37\xf0\xe1\x90\xee\x6d\x34\xde\xc0\x87\xa3\x0f\x6e\xa3\x71\x72\x05\x61\xd5\x23\xd7\xa1\x3a\xed\xf4\x69\xf1\x6e\xf0\x4d\x30\xa8\xcc\x43\xe8\x03\xf1\x86\xd1\x07\xb2\x0c\xa1\x0f\xc4\x16\x36\x41\xde\x28\xb1\x40\x55\xbe\x95\x87\x20\xa9\x06\xaa\xf1\xad\x3c\x04\x49\x3e\x50\x85\x5d\xab\xa2\xe1\xa5\xe1\x10\x25\xd6\x67\x91\x71\x2d\x36\x16\xe9\x01\x6a\x16\x5c\x03\x0a\x8f\xd4\x10\xa0\x27\x66\x9d\x6f\xa7\x71\x25\x1f\x8e\xb6\xbf\x9d\xc6\xc9\xf6\x87\xc2\xfb\x1a\x7a\x5c\x01\x5d\x0d\xc1\xbd\x3d\x2d\x46\x46\x28\x7d\x0c\xfc\xc6\x7e\xf6\xd6\x78\x2d\x81\x2b\x79\x08\x6a\xf5\xb5\x04\x4e\x8e\xdd\xca\x91\x55\xdc\xc8\xcd\x0f\x93\x80\x7b\x9f\x1c\x5e\xb6\x81\xa0\xfc\x8f\xb1\x0b\x26\xe8\x3b\x1c\x26\x91\x4f\x36\x2d\x33\x01\x9b\x8d\xa4\x1d\x31\xf1\x0a\x07\x45\xfb\xe4\xd9\x46\x56\xa7\x2a\xc2\x10\x07\x6f\x1f\xf1\xb1\x3d\xfe\xbe\x4e\x1e\xdd\xc8\xf5\x37\x85\xa3\x98\x0c\x45\x8e\xe5\x87\x7d\xd2\xc6\xe6\x48\x4b\x8d\xed\x79\x67\x02\xc0\x3e\x21\xdd\x01\xae\xef\x84\x9e\x1b\xb9\xb2\x1a\x5f\x2f\x38\x93\x8f\x06\xb6\x00\xc9\x49\x0b\xd8\xf5\x3e\xa6\x88\xd2\xc5\xbb\xc9\xc5\xb1\x58\xd2\x3a\x99\xd6\x52\x86\xfb\x2e\x79\x68\xe0\xbe\x5e\x7a\xf3\x75\x12\x5f\x4d\xb7\x74\xd4\x15\xed\x78\x43\xfe\x60\x94\xee\x9b\xfa\x0a\xe3\x5b\x4b\x2d\xe3\x4b\x9f\xa8\x81\x7b\x07\xd5\x1d\x72\x35\xfa\x2a\xbb\x69\x68\x85\x37\x05\xe9\x31\xc0\xb4\xeb\xa0\xbf\x97\xf4\xdb\x06\x18\xc2\xfa\x90\x3a\xc5\x97\x2b\x72\x80\xf5\x76\xd7\x1d\xb2\x52\x3a\x6b\xba\xa5\xaf\x42\x1b\x81\x75\xd4\xc0\xae\xeb\x94\xbe\x9b\xee\x9b\x7a\x09\x63\x5a\xcb\x05\xe2\x4b\x89\xa9\x81\xfb\xfa\xea\x1e\xf9\x37\x7d\x95\xdd\xd2\x5d\x57\x34\x05\x9b\xb8\x06\xa6\x5d\x67\xea\xbd\xa4\xdf\xd4\x69\x18\xeb\x30\xd3\x85\x2f\xff\x27\x84\xf6\x75\xd9\x3d\x72\x8d\x7a\xea\xba\xa5\xc7\xc2\x1b\x82\x75\xd8\xc0\xb2\xeb\x20\xbc\x8f\xf2\x9b\xfa\x4b\x67\x9c\x9e\xf7\x34\x33\x57\x14\xa1\x57\xeb\xe5\x91\x78\xfd\x3d\x82\x04\xcb\xe6\xe9\xac\xcf\xfa\x22\x0f\xe3\x5a\x90\xec\x03\x5b\xab\x59\x45\x39\x4b\x71\x8a\x21\xf1\x1c\x12\x58\xc9\x73\x9e\xd1\x4a\x9e\x31\x54\x0d\x26\xfb\xb6\x2a\x2e\xdd\x90\x7c\x59\xac\x73\xe0\x6d\x80\x21\x65\x01\x48\x37\x8f\x65\xf8\xb4\x56\x61\x56\x5b\x67\xeb\xfd\xeb\x56\x4b\x88\xa0\xee\x2a\xac\xa7\xf3\xd5\xef\x9c\x88\xcb\xfd\xeb\x02\xc5\xdb\x0c\x48\x5f\xa9\xb8\xec\x79\xce\x4b\x2b\xab\xe8\x70\xf8\x7b\xeb\xcf\x02\x3e\xee\xd6\x6b\xab\x0d\xba\xe8\xff\xdd\x9e\x9f\x35\xec\x1e\xc3\x38\x86\x26\x04\xa6\xb1\x7f\xbb\x54\x1d\xfa\x2a\xbb\x7e\x7a\x5d\x7c\xb5\xf3\x9b\x42\x8a\x71\x61\xdc\x7a\x98\xeb\xf9\x14\x90\x77\x0b\x18\x5a\x7b\xd6\xd1\xb6\x28\x16\xd8\xdb\x1a\x12\x84\xc3\xe7\x2e\x1e\x03\x1e\x6d\x18\x7d\x57\x25\x49\x86\x97\xd3\xf4\xfb\x21\x89\x5a\x2e\x1c\xf2\xa2\xeb\x3b\x98\x14\xf5\x89\x7c\xa8\x6a\x92\xe6\xdd\x6b\xf4\x43\x34\x4f\x58\x97\x88\x0f\xbb\x68\x3a\xd7\x18\x56\xb7\xdf\xf9\x5f\xf6\x35\x20\x58\x77\xc0\xe3\x23\x3e\x56\x56\x26\x2b\x32\x9f\xc3\xfe\xd2\x75\x55\x09\x24\xa8\x52\x61\xd6\x35\x25\x0d\x29\x53\xf0\x82\xaf\x6e\xbd\x90\xea\x87\x71\xc0\x92\x1d\xa3\xc3\xfb\x5c\x65\xa4\x88\xd9\x7b\xd4\x58\x36\x1c\x0d\xcc\x30\xb4\x87\xfc\x85\x67\x7f\x18\x8c\x4e\x03\x8c\xab\xcb\x00\xa9\xd4\x01\xb3\x64\x95\x3c\x9a\xef\xd2\x60\x86\x18\x8e\x41\x59\x16\xb7\x69\x53\x15\x05\x6b\x7f\x57\x5d\xd2\x13\x43\xbc\x74\xbd\xee\x98\xcd\x9b\x1e\x48\x46\x23\xd1\xd4\x2c\x27\x45\x75\xd4\x84\x0b\x53\xf7\x6a\xdf\xd8\xfb\xff\xd3\x85\x78\x82\x01\x7f\xce\x41\xfe\x89\xc2\x42\x40\x0f\x45\xb3\x4e\x01\x5c\x90\x8e\xf6\xe3\x39\x9e\xaf\x7e\xc7\x13\xb3\x9e\xdb\x00\xa0\x6a\x1c\xc6\x0b\xa0\xcb\x2e\x2f\xc7\x24\x87\xd0\x49\x46\xd9\x4d\xc6\x78\x4d\xbc\x8c\x26\x1f\x11\x05\x06\x5a\xaa\xd4\xe4\x05\x2a\x91\xfa\xfa\xaa\xbd\x7d\x61\xb7\x0f\xf7\x26\xc4\xd4\xa9\x3f\xa5\x61\xe4\x58\x62\x94\xc4\xc3\xd5\x1e\x52\xee\x5b\x76\x48\xf2\xdd\xb4\xc8\xeb\x5d\x34\xcc\x9a\xd6\x24\x87\x96\xdb\xf3\xdc\x76\xbb\xc5\x4b\x8c\x89\x63\xfe\x11\x9f\x16\xf4\x21\xe6\xbe\xdb\xb7\xa8\x5f\xfa\x59\xc2\x24\x8b\x5d\xed\x73\x82\xea\x02\xed\x1b\x9b\x35\x55\x7d\x77\x0b\xb4\x4c\x1c\xbd\x91\x24\x09\xce\x02\x37\x26\xbf\x78\xac\xbb\x61\xdc\x5d\x74\xf2\xd2\x4b\xc5\x39\x47\x08\x3a\xbd\xab\x2e\x5f\x11\x02\xbe\xd2\x6c\x3d\x5d\x2e\x4c\x77\x69\xec\x66\xe3\x77\xec\x05\x40\xbc\x02\x30\x19\xc1\x10\x78\x3c\x37\x95\xde\xbc\x83\x88\x5f\x44\x95\x0f\xf2\x18\x42\x51\x37\x37\xd1\xd1\x82\xde\xe7\xe4\x98\xe8\xdd\x40\xd1\x5e\x98\x76\x5e\xf9\x20\x8e\x18\x38\x26\x01\x41\x7b\xba\xef\xca\x4f\xec\x91\x33\xd7\x9e\x82\xf6\xa8\x8e\x8b\xc5\xe1\xd1\x34\x9c\xe2\xf0\x60\xbb\x1b\x9d\x39\x85\x9f\x86\x9f\x36\xbe\xa1\x6d\x7c\x96\xdc\x93\x26\x3e\x53\xd2\x5e\x1a\x73\xb1\x64\xad\x1d\xe2\xed\x76\x2b\x3c\x3c\x61\xee\x56\xc2\xab\x96\x7d\xb8\xb2\x1e\x3f\xe0\x95\x88\x8a\xc5\xe3\x55\x1f\x22\xf5\x64\x55\xa4\xbd\x59\x65\xd9\x5a\x59\xcf\x3a\x91\x2f\x4c\x49\xfd\x61\xaf\x92\x71\x3b\xcd\x1f\x98\xb2\xad\xab\xc3\x02\xad\xc4\xe5\x50\xdc\x04\xbd\x0b\x05\xd6\xeb\x15\x8e\xb0\xe4\x77\x91\x24\xda\x0b\x56\x58\xc3\xb7\xdb\xb9\xd1\xf0\x42\x6f\xf4\xd6\x20\x32\xed\xaa\xaa\xe8\x72\xd3\xd0\xc1\x6e\x02\xd6\x6b\x93\xe0\x8b\x5c\xe6\x54\x1f\xc8\x39\x2f\x5e\x77\xd1\xfb\x7f\xa5\xc5\x33\xed\xf2\x94\x44\xff\x46\x2f\xf4\xfd\x24\x52\x1f\x26\xd1\xbf\x34\x39\x29\x26\x51\x4b\xca\x36\x6e\x69\x93\x1f\xcc\xeb\xc1\xd8\xf3\x87\x4b\xcc\x4d\x9f\x2e\x7d\x1e\xaf\xcb\x26\x8a\xe6\x8e\x19\xc3\xad\x69\x0c\xb7\x26\x81\xae\xaa\x75\x23\xb0\x92\x37\x5a\x35\xab\x05\x96\x29\x12\xd3\xca\xbb\x28\x74\xc2\x1a\xd8\x08\x2e\xc8\x60\xe8\xaf\x18\xc1\x35\x53\x36\xba\xaa\x45\x78\x8e\xf3\x12\x79\x41\x6e\x9e\x60\x4f\x5a\x3e\x5c\xf1\x22\xc7\x35\x79\xe8\xc4\x72\xc8\xbd\x9d\x2a\x79\x25\x4d\x53\x7d\xf5\xe8\x33\xf6\xde\x48\x62\x2f\x6e\xf1\xdb\xf7\x22\x6b\x23\x33\xe2\x98\x4a\x20\x5c\x20\x5e\xc1\x8a\xc7\x3e\x74\xb1\xeb\x93\xa6\x34\x86\xfc\xe1\x4a\xeb\xd2\x31\xe2\x36\x00\x36\x78\x7a\x4e\x84\x97\xe1\x49\x47\xcb\x5f\x31\xb3\x3d\xde\x93\x1d\xae\xf2\x81\xb2\xc1\x93\x4f\xde\x8b\x1d\x27\x2b\x7c\xa3\x9d\x77\x8c\x9d\x71\x93\x8f\x66\x2f\x13\x26\x23\xfc\x2c\xae\x87\x15\x57\x27\x41\x4e\xa0\x7b\x19\xc6\x4a\x22\x99\x79\xd4\x0f\xef\xf9\x38\x11\x66\xc5\xc5\xcb\x4d\x9a\x8b\xf1\x61\x84\x59\x9d\x9c\x38\xf5\xd7\xf2\xbb\x75\x6d\xf1\x88\xe6\x4d\xec\xf8\x95\xc6\xa9\xbc\x77\x63\xa7\xae\x6a\x15\x1e\x1a\x8b\xc3\xe2\xeb\x8e\x35\x1e\xf9\x80\xd6\x7c\xb3\x36\xdd\x78\x30\x15\xdf\x67\x72\x5f\x86\x4f\xee\xe0\x1d\x4d\x38\x77\xa8\x47\xa3\xf4\x37\x8d\x9b\x33\x29\xfe\x6e\xcb\xdc\x34\x4d\xdf\xbc\xcc\xf5\xb8\x95\x89\xed\x29\xce\x91\x95\xad\x1b\xd6\xd0\x22\xe5\xba\x68\x5a\x0a\x83\x0a\x12\x70\xf0\x54\xb4\xf1\x8e\x81\x02\xc7\x44\x4b\x9d\x8d\x80\x2a\x3f\xc4\x58\x87\xd8\xa0\x60\x89\xa7\x94\xf2\xa1\x6f\xa6\xf6\x24\x2b\x92\x63\x66\xe9\x8e\xcb\x6f\xfa\x7f\x23\x8b\xd3\x7d\xff\x0f\xe9\x2b\x65\xe5\x23\x73\x64\xea\x11\x18\x10\xb9\x66\xbc\x18\xc0\x4f\x53\x66\x40\x26\x91\xf1\x61\x47\x0e\x9d\x77\x90\xdb\x8e\xf7\xdd\x7d\x19\x9d\xa3\x08\x49\xdc\x32\x73\xb5\x07\xb0\x2f\xa4\xb1\x8b\xde\xbf\xb7\x4d\x1f\xa6\x13\x5d\x55\xeb\x55\xca\x94\xd3\xc2\x06\x79\x26\x1f\x09\x82\x4c\xff\x5a\xfc\x09\x96\x98\x43\x64\xf5\xd1\x36\xc3\x56\xe6\x10\x8b\x55\xd0\x60\xa0\x45\x08\x87\xea\x91\x3a\x29\x95\xe8\xbd\x83\x63\x2d\xf3\xd5\x18\x27\x6c\x78\x42\xb1\xd9\x2e\x8c\x92\x8e\x3e\xd6\x75\x91\xe9\x8e\x8a\x2e\x34\xad\xcc\x2b\x36\xe6\x63\x84\xb0\x8a\x88\x4d\x89\x48\x18\x18\xaf\xbc\x74\x76\x75\x89\x79\x99\xe0\xb5\x59\x02\xbb\x51\xc9\xe0\xd0\xc3\x27\x70\x5d\x92\x7a\xa1\x2d\x4a\x2f\xb7\x40\x66\x32\x90\x74\x93\x9e\x8d\x33\x0d\xcf\x85\x01\x83\xed\x52\x33\xf9\xde\x6e\xb8\x9e\x21\x2c\x68\xee\xa9\x2e\x35\x58\x34\x2e\x33\xc0\x29\x90\x98\xcc\x8a\xa3\x2f\x7a\xc6\xc4\x15\xc6\x2d\x10\x57\x4a\x9a\xea\xd2\x52\x73\xd7\x4a\xc6\x15\x4d\x30\xb0\xa6\xf6\x46\xff\xe5\x46\xb9\x6b\xa7\x4c\xa7\xf7\x34\x0d\x4d\xfe\x2f\x5d\x40\x6c\x53\x4a\xbe\x24\x29\x5e\x01\x57\x5e\x97\xb1\x15\xe5\x02\x1b\x83\xf1\xb1\xfe\x24\x1f\x18\x47\x0b\xe5\x5b\xe3\xba\xb7\xa8\x07\xc4\x48\x51\xf0\x37\xec\xd5\x5e\x4e\xbc\xc8\x3e\x46\x93\xe8\x83\xbd\xd9\x16\x2f\xb2\x48\x84\xcb\x9c\x72\x0c\xdd\xb6\x5b\x5b\xef\xab\xfb\xb7\xee\x10\x78\xc7\xf6\x1d\x46\x19\xb8\xb3\x07\x92\xd2\xf8\x39\x6f\xf3\x7d\x5e\xb0\x68\xd5\xb0\x01\xf5\x6e\xac\x5c\xd2\xa9\x69\xd3\xd6\x94\x67\xa4\x64\x2f\x8d\xb0\x52\xeb\xab\x7a\xe2\x1f\x11\x96\x48\x68\x39\x95\x6f\x8d\xa0\x30\xa5\xcc\x21\x09\xd7\x29\xde\xed\xbd\x45\xf6\xa1\x1f\x02\x7c\xd8\x7f\x1c\xc4\xe4\x05\x0b\xe1\x53\xbc\x45\x82\x82\xd4\x0d\x7d\xbe\x9a\xcd\x38\x90\xcf\xf8\x3a\x46\x7d\xa2\xe4\x8d\xe8\xeb\x72\xb7\x03\xb8\xf1\xd7\xb4\x26\x09\x68\x49\xa2\xb5\x02\x1f\xd4\xce\x46\x94\x3c\x41\xba\xf9\x59\x89\x1e\x7f\xcf\x06\x27\xcf\x8d\x82\xbe\x21\x71\x6d\x7d\x63\x0b\x6b\x60\x8f\x7d\x55\x00\x56\x7c\xa0\xaa\x5a\x35\x79\xfb\xc9\xba\xd4\xd5\xe8\xe1\x11\x19\x00\xd5\x0f\xad\x1c\x8e\xea\xb1\xb6\xf5\x53\x69\x53\x99\x33\xe0\x6d\xe7\xc5\x56\xe6\x03\xd0\x57\xbd\xf5\x8c\x9c\xd8\x41\x1e\xa9\x5e\x33\xed\xbe\x7e\x47\xd4\x6c\xf0\x20\xd0\xf0\x47\xcb\x78\x7f\x5a\x5b\x40\x51\x32\x3c\x66\x05\x0e\x6b\x25\xc9\xec\x23\x93\x79\xf8\x33\x62\x77\xae\x40\x34\x63\x20\xcf\xaa\x9b\xb0\x09\x3d\xea\xaa\x7a\xc2\xb7\x3c\xf9\xcf\x43\x53\x9d\x3f\x58\x55\x7f\xe4\x4f\x7d\x55\x66\x09\xab\xfc\x63\xe0\x8b\x62\x5d\x15\x89\x19\xe6\xb6\xa6\xc9\xde\xae\x9b\xea\x98\x67\xbb\xff\xf2\x3f\xff\xd8\xd7\xf3\x67\x69\xe0\xa6\x7f\xca\xd3\xa6\x6a\xab\x43\x37\x55\x55\xb6\x1d\x69\xba\x3f\xf4\x6a\xd7\x76\xcd\x0f\xdf\x7f\xf7\x90\xf0\xff\xfb\x9e\x55\x47\xcb\x0c\x94\x25\xaa\x2c\xfa\x6f\x02\xff\xcf\xaf\x35\xfd\x61\x66\x36\xaf\xa1\x35\x65\x87\xd5\xd8\xff\xc6\x2f\x4e\xdd\x1a\x46\x1e\x0c\x06\xf3\x11\x23\x8f\x86\xbc\x51\xed\xb8\x88\x50\xe9\xad\xee\xa2\x76\xb7\x57\xf0\x66\xb5\xe3\xca\xe5\xd0\xbc\xd5\xdb\xd5\x2e\xb0\x69\x77\x50\xbb\xc4\xa3\x76\x0f\xf7\x56\xbb\xe1\x78\xa2\x59\xe0\x4c\x58\xee\xdc\xcd\x1b\xdb\x65\xd5\x8e\xf8\x20\x7b\xae\xd6\xfc\x32\x3d\x16\xaf\xf5\x29\x4f\xab\x32\x4e\x4f\xf4\xb9\xa9\xca\xd8\x9c\x1e\x3d\xa0\xbc\x1f\x35\xaf\x4b\x41\x33\x40\xd3\x53\xd0\x4b\x43\x1c\x06\xb1\x3a\x56\x01\xf8\x95\xb6\xf4\xca\x4b\xb6\x7e\xc1\xfd\x9a\x5b\xda\xa8\xf8\x12\x67\xb5\x7d\x91\x0c\x10\x7d\x0b\x17\x96\xb3\xd2\x5e\x54\xa2\x52\xb9\x23\xa3\xd5\xaa\xe2\x03\xde\x6a\xaf\x91\xba\xda\x7c\xd6\x4f\x89\xc8\xbf\xad\xa0\xb6\xb5\x85\xa1\x76\x26\xd0\x45\xa4\xaf\xfe\xdd\x9e\x1e\xaa\x86\xea\x21\xce\xef\xff\x32\x4f\x16\xdb\xef\x03\x1a\xe7\x46\x27\x36\x7a\x5e\x66\x79\x4a\xba\xaa\x69\x3d\xba\xa6\xc2\x8e\x09\x12\xc1\x1a\x76\x7f\x56\xc0\xaf\x5a\xf3\x42\xf7\xdb\x82\xf2\x10\x00\x87\xc3\xbd\x2b\xd7\x03\x86\x18\xf7\x45\xae\x7b\xf3\x50\xf9\x35\xb7\x5a\xef\xcf\x59\xa2\xc7\xf8\x67\xe0\x88\x55\xdf\xae\x5e\x76\xb1\x3a\x3a\xe4\x3d\x8c\x0c\x77\xd4\xa2\xbf\x6c\xf1\x42\xdd\x74\x27\x1f\x1d\x7b\x3d\x7a\xb8\x51\x6e\x0c\xa0\xda\x0d\x64\x00\x57\x29\xb2\xbd\x73\xa3\xbd\x73\x64\x4f\xc3\x7f\x6f\x73\x50\x35\x9e\x0c\xda\xa3\x28\x2a\x31\xf4\xef\xa0\xda\xcc\xf5\xa8\xeb\xca\x50\x1b\x78\x65\x85\x0f\x28\xe3\x62\x84\x75\x11\xe2\xae\xae\xb9\xb3\x99\xea\x20\x9b\x46\x09\xe8\xa1\x08\x04\xb5\x69\x43\x69\xc9\x63\x41\xea\x8c\x94\x76\x36\xec\xd7\x9a\x58\xde\x3a\xb3\x0c\x47\xbe\xc4\x09\x35\xa9\x25\x0b\xfd\xc8\x9a\x34\x73\x62\x1f\x19\xae\x96\x16\xea\x9c\xd7\x1b\x9b\x09\xe7\x16\x66\xe7\xcd\x09\x65\x75\x5d\x45\x61\x13\x8a\x56\x93\x9a\x44\xd0\xaa\x80\xf2\x4b\x3d\x9f\x73\xe3\x25\x14\x5b\xfe\x65\x6a\x2d\x2a\x22\xc3\xee\xbe\xd3\xb3\x3f\x0c\x61\x0e\x75\xb8\x32\x96\x4f\xc9\x3e\x0d\xdf\x78\xf0\x79\x12\x8d\x40\xf1\xf9\x80\x79\x22\xfd\xc7\xae\xaa\x8a\x3d\x69\x34\x64\xf9\x4d\x80\x46\xd3\xb4\xa0\xa4\x39\xe4\x2f\x0a\x4a\x7d\x00\xd4\x7a\x91\x92\xbc\xa4\x4d\x7c\x28\x2e\x79\x36\xc0\x1a\xdf\x07\xaa\xb2\x40\x80\x6a\x44\x06\xb0\xac\x88\x4f\x55\x93\xff\xdc\x97\x14\x51\x36\x10\xb6\x0a\x00\x33\x2c\xce\x0a\x4a\xf9\x07\x5d\x4e\x3e\x18\x40\x0a\x1e\x50\x55\xb8\xda\x47\xc5\x6a\x49\x9e\x15\x44\xff\x1b\x50\x29\xc9\xf3\x9e\x34\xea\x4e\x20\x04\xd3\xbe\x43\x5a\x7d\x01\x3f\xa0\x0c\x24\xa4\x7f\x37\xc0\x0d\xb2\x43\x71\x4d\x8e\x1a\x15\xfe\x37\x28\x96\x17\x14\x15\x05\xf0\x49\x81\xa9\x2d\x0e\xf1\x9b\x17\x08\x57\x4c\x7f\x4f\xd9\xdc\xe4\xb8\x5e\x85\x4d\xcd\x34\x94\x30\x48\xe5\xdc\xba\x32\x10\x09\x51\x15\x54\x07\x86\xde\x0e\xe8\x60\xad\x1f\xad\xee\xb2\xbb\xc7\xea\x0f\x63\xb3\xbb\x17\x06\x7b\x41\xfa\xa4\xa6\x2c\x36\xd9\x81\x23\xd3\xf6\xce\xbd\x6e\xda\xf4\x2b\x1e\xf1\xb0\xb8\x57\x3b\x5c\xfa\x8b\xdd\xf0\xa2\x5b\xf4\x4f\xf9\xb9\xae\x9a\x8e\x94\x9d\x06\xad\x62\x52\x02\x98\xfd\x6d\xc3\x9e\x72\x71\xbd\x40\xdb\x19\x42\x00\xdb\x93\xd8\xff\xd3\x1b\x83\x40\xe6\x25\xdb\x6d\x90\x2f\x73\x5b\x3b\x0f\xea\x94\x55\x3f\x7f\xab\xfa\xfb\xc9\x6b\x17\x25\x9f\x93\x88\x00\x5f\xc2\x38\xba\x60\x4d\xf9\xb8\x9b\x84\x1e\x78\x00\x71\x51\xce\x47\x50\xb3\xc9\xe1\x90\xbf\x60\xf7\x40\xa4\xb3\xf1\xed\x37\xf1\xb9\x8d\x9f\x73\xfa\xb5\xc7\x84\x3e\x5e\x46\x9f\xf3\x94\x72\xbf\x43\x92\x13\x92\x89\x8b\xe3\x24\x52\x7f\x9c\x33\xf0\x47\x7b\x06\x7f\xbc\xb4\x41\x4c\x0e\x54\xb9\xd2\x01\x0a\xc5\x31\xe6\x2e\x37\xf6\x4d\x40\xf7\x7a\x3e\xf0\x62\x91\x38\x67\x36\x09\xf5\x0d\x21\xd1\x9e\x2d\x12\xed\xd9\x26\xa1\xbe\x21\x24\x5e\x5a\x8b\xc4\x4b\x6b\x93\x50\xdf\xb0\xb1\x86\xcb\x6a\x38\x36\x2f\xcf\xbe\x45\xbb\xcd\x7a\xa3\x7c\x42\x43\xf2\x3e\x6d\x67\xae\x03\x33\xb1\x6e\x2c\x56\x2c\x41\x9b\x11\xb8\xb8\xa9\xbe\x22\x35\x64\x00\x6d\x12\x75\xa7\x31\x2a\x29\x2d\x0a\x8b\xcc\x95\x8d\x07\x02\x1d\x13\xc1\xb5\x94\x79\x8f\x19\xa4\xc5\xc7\xfb\xd0\x46\x99\xd7\x8a\x46\xea\x31\xaf\x92\xf0\x35\x84\x3a\x2c\xb9\xdd\xce\xac\xca\xe5\x6d\x8d\xeb\xf4\xc5\xc2\x72\xe8\x0b\x0e\x37\xaa\x2f\xbd\x2d\x01\xfa\xe2\xa0\x12\xa2\x2f\x37\x49\xe4\x76\x25\xba\xad\xba\x37\x68\xd6\x5b\x2a\xbc\xa3\xba\xf1\x0b\x3c\x46\xe5\xb3\xd9\x76\x6b\xd5\x7e\xce\x6e\xd1\x37\x0b\xcb\xa1\x6f\x38\xdc\xa8\xbe\xf5\x13\x19\xd0\x37\x07\x95\xeb\xf4\xed\x1a\x91\xdc\x43\xe1\xae\xaa\xef\x2e\x1a\x77\x43\x8d\x77\x54\xb9\x19\xbb\xc7\x63\x54\x24\xaf\x8d\x5d\xa7\x5d\x16\x96\x43\xbb\x70\xb8\x51\xed\xea\x7d\x26\xa0\x5d\x0e\x2a\xd7\x69\x97\xa3\xf5\xf7\x50\x24\x17\xe9\xbb\xe8\x8c\x9f\xf8\x9b\xd5\x03\x9f\x68\xb9\xdb\x6c\xbb\x1d\x96\xa7\xf5\x36\x03\x2b\xaa\xb1\x66\xab\xeb\xaa\x19\x1f\x55\xa2\x1e\xcb\x4a\x5d\x57\x8f\xd6\x13\x82\xa4\xa5\x9a\x6e\x92\xaa\xf3\xea\x26\x17\x07\xc8\xc3\x3c\xd8\x01\x1e\x21\x71\xe5\xc0\xc5\x10\x1d\x63\xd7\x09\x3a\x3a\x7c\x19\xa6\x36\x82\xdd\xb4\xdc\x83\x58\x47\xbe\xda\xef\x77\x49\xed\xda\x01\x6f\x60\x83\x31\xfd\x46\x3e\xae\xb6\x0e\x28\xfe\xfd\xe4\x72\x0f\x83\x02\x88\x8b\x01\x82\xf5\x3c\x3e\x46\xfe\x4f\x00\x00\x00\xff\xff\x47\x37\xb0\x07\xc9\x28\x02\x00") - -func cssBootstrapMinCssBytes() ([]byte, error) { - return bindataRead( - _cssBootstrapMinCss, - "css/bootstrap.min.css", - ) -} - -func cssBootstrapMinCss() (*asset, error) { - bytes, err := cssBootstrapMinCssBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "css/bootstrap.min.css", size: 141513, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _faviconIco = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5a\x0b\x70\x55\xc7\x79\xde\x7b\xce\x45\x12\xb2\x90\xc4\xc3\xe6\x61\x3b\x90\xf8\x31\xc4\x19\x6c\x32\xe3\xc4\x34\xe3\xc6\x34\x6d\xed\xd4\x69\x62\x32\x49\x9a\xa6\x75\xea\x36\x33\xee\xd8\x9e\xb4\x75\xdc\x7a\xa6\xc5\x31\x02\xa6\xd3\x84\x47\x28\x6f\x3b\xc6\x3c\xcc\xeb\x9e\xbd\x08\x21\x04\x92\x28\x12\x0e\x08\x5b\x3c\xec\x02\x92\x78\x08\x21\x09\x41\x25\x24\x10\xe8\x71\xb5\xe7\xdc\xd7\xb9\xf7\xef\xfc\xff\x9e\x73\x74\x25\xdd\x97\x24\x82\x33\x1e\xce\xcc\x3f\x7b\xee\x9e\xdd\xff\xff\xf6\xdf\x7f\xff\xfd\xf7\xdf\xcb\x98\x8b\xa9\x2c\x3f\x1f\xcb\x19\xec\x15\x37\x63\x5f\x67\x8c\xcd\x98\x21\x7f\x6b\xf9\x8c\x6d\x72\x33\x36\x7b\xb6\xf5\xfb\x11\xc6\x9e\xbd\x97\xb1\x99\x8c\xb1\x7c\x6c\xc7\x64\x3d\x3d\x6e\x76\xdb\x1f\xe1\x75\xab\x42\x53\x7e\x22\x3c\x6c\x91\xf0\xb0\x02\x22\xae\x14\x08\x8f\xab\x40\xec\x64\x92\xf0\x5d\x53\xfe\x59\xec\x64\x5f\x15\x1e\xa6\x08\x4f\x7f\x7f\xdf\x7a\x96\xa1\x97\x3c\xb8\x3f\x78\xfa\x0d\x08\x9e\x5d\x08\xc1\xda\x5f\x82\x51\xf6\x65\x30\xca\x67\x41\xf0\xcc\xbf\x11\x19\x07\x1e\x07\xbd\x78\x2a\x18\x65\x8f\xb5\x0b\x4d\x7d\xad\x6f\x07\x73\x0b\xcd\x45\xfd\xbb\x17\xb3\x0c\xa3\x62\xce\xfe\xa8\xd1\x0a\xf8\x98\x6d\x25\x10\x38\xfe\x53\x88\xdc\x3a\x01\x66\x6b\x11\x11\xbe\x07\x8e\xfd\x2d\x84\x2e\x2c\x05\xff\xd1\x79\x3d\xc2\xc3\x7e\x20\x78\x06\x13\x5c\x89\xe9\xdf\x06\xd1\x50\x2f\x04\x4e\xfc\x0c\xcc\x6b\xa5\x60\x1c\xfd\x21\x74\x6c\x1e\x47\x84\xef\x58\x17\xf8\xe4\x65\x30\x6f\x54\x81\x71\xe0\x89\xea\xbe\x2d\x6c\xd2\x80\xfe\xfe\x76\x30\x6f\x1c\x81\x60\xed\x7c\x08\x9e\x5d\x0c\x2d\xeb\xc7\x41\xed\xd2\x29\x50\xbb\x74\x32\xbd\x63\x1d\x7e\xc3\x36\xa1\x73\xff\x19\x14\x1e\x36\x4f\x78\xc7\xc4\xf4\xef\x80\x70\xd3\xfb\x10\x6e\x5c\x0f\xdd\x65\x73\xe1\xdc\xf2\x89\xd0\xb1\x25\x0f\x6e\x7c\x90\x07\xe7\x7f\x33\x81\xea\xf0\x5b\xb8\x79\x13\x61\xd0\x8b\xa7\x2c\x09\x7c\xfc\x57\xac\x7b\x11\xf6\x7f\x6a\x5f\xd4\xb8\x06\xa1\xfa\x15\x10\x6e\x7a\x0f\x3a\xb4\xc7\xa0\x61\x55\x3e\x04\x8f\xfc\x39\x84\x3e\xfa\x4b\x68\x5c\x93\x4f\x75\xf8\x2d\x74\x71\x05\x44\x45\x13\xea\x54\xeb\x7c\x85\xa9\x48\xf8\x1e\x15\xcd\xf4\xcd\xee\x7f\x71\x65\x3e\x04\x3e\x7c\x86\x78\x5c\x5a\x1d\xd3\xbf\x7e\x05\xa0\x2c\x94\x89\xb2\x03\xd5\x3f\x66\x88\x05\x31\x21\x36\x1b\xff\xd9\xe5\x13\xe1\xda\xc6\x5c\x68\xdf\x94\x4b\x63\x71\xf0\x37\xbd\x0f\x38\x56\x1c\x33\x8e\x1d\x75\x80\xba\x40\x9d\x0c\xd6\x5f\xcd\x92\x29\x44\x83\xf5\x87\xba\x76\xfa\x73\x85\xe1\x5c\x18\x07\x9e\x38\x86\x18\x68\x8e\xac\xf9\xbb\xbe\x39\x87\xc8\x99\xbf\x13\x3f\xa3\x39\xc6\xb9\x8e\xed\x4f\xb6\xe0\x61\x3f\x40\xdb\x40\x1b\x41\x5b\x19\x62\x3f\xc7\x7f\x4a\xb6\x85\x0f\xda\x9a\xdd\x9f\xd6\x80\xe6\x62\xd2\x26\xd5\xd7\xd0\x46\xc9\x56\x0f\x3c\xde\x6f\xbf\xe5\xb3\xc8\xa6\xd1\xb6\xc9\xc6\x4f\xbf\x01\x68\xf3\x68\xfb\xce\x3a\xf2\x30\x26\x76\x32\x85\xd6\x08\xae\x95\xc1\xeb\x87\xd6\x14\xb3\x69\x11\xad\x39\xaf\x5b\x1d\xed\xfa\x05\x60\x8c\x65\x32\xc6\x54\x8b\x5c\x31\x64\x3d\x0b\x63\xe8\xb0\x45\x2d\x56\xdf\x99\x96\x8f\x99\x1b\xeb\x67\xf2\x47\x8b\xea\xf3\xf9\x08\xae\x22\xe5\x09\xae\x4e\x17\x5c\xfd\xc2\x28\x69\x8a\xe0\xca\x58\xb4\x3d\xdb\x17\xa6\x29\xff\x5f\x04\x57\x5b\x85\xe6\xba\x92\x90\xb8\x1a\x43\x4a\x4c\xbd\x62\xd7\xb7\x08\xae\xd6\x0b\xae\xfe\x8f\xe0\xea\x9b\x38\x1e\x1f\x1f\x4b\xfe\x34\xb5\x7c\xa5\x40\xec\xca\x02\xbd\x64\x3a\xe8\xfb\xbe\x38\x94\x4a\xa6\x83\xf0\x66\x82\xd0\x14\x10\x9a\x0b\x44\xe1\x38\x5c\x2b\x44\xf8\x4e\x75\x5c\x01\xe1\x75\x03\xf2\x11\x9a\x2b\x2a\xb8\x52\x2b\xb8\x3a\x4f\x68\x8a\x92\x0c\x03\xc9\xf7\xb0\x02\xa3\xfc\x2b\x10\xe9\xae\x85\xa8\x7e\x15\xa2\xfa\x95\x18\xba\x0a\xa1\xb3\x8b\x49\xbe\xbe\xe7\x5e\x08\xfe\xef\x3f\x81\xd9\x51\x09\x91\xbe\x4b\x44\xf8\x8e\x75\xf8\x4d\x70\x37\x04\x3e\xfe\x11\xf9\x14\xbd\x68\x3c\xe2\xe8\x14\x5c\x7d\x49\x78\x99\x2b\x11\x06\x47\xfe\x81\xd9\x10\x0d\xde\x82\xc1\x8f\x79\xe3\x30\xe8\x7b\xa7\x81\x51\xfa\x28\x98\xd7\xca\x00\xa2\xa6\xfc\x80\x65\xcc\x3b\x7e\xc3\x36\xfa\xde\x07\x08\x93\xd9\x5a\x0c\xfa\xfe\x87\x41\x68\xac\x43\x70\xf5\x59\x92\xe3\x1d\xea\x1a\x92\xc9\x8f\x06\x3a\xc1\xff\xe1\x5c\xda\x67\x91\x27\x3e\x91\xde\x0b\xe4\xaf\xfc\x47\x5f\x20\xc2\x77\xac\x23\xac\x1d\x95\xd4\xd6\xff\xe1\x9f\x10\x2f\xfa\x5d\xf2\x05\xc4\x70\x4c\x70\xf5\x7e\x94\x35\x1c\xf9\xa1\xc6\x77\x68\x3e\x43\x17\x57\x4a\xfe\xe8\xc3\xcb\xbe\x8c\xfc\xa0\xcf\x23\x09\xdf\xb1\x0e\xbf\x51\x9f\x8b\x2b\x65\x9f\xc6\x77\xe8\x37\xee\x4f\x62\xd7\x58\xb4\x8f\x05\x3e\x9e\x35\xc4\x1e\x13\xc9\x8f\x06\xbb\xc0\xa8\xfc\x23\xf0\x1f\xfa\x63\x88\x86\xba\x21\x72\xeb\x24\x18\xfb\x1f\x82\xde\x1d\x2e\x68\xdd\x30\x0e\x1a\x56\x4f\x24\xc2\x77\xac\xc3\x6f\xd8\x06\xdb\x62\x1f\xec\x8b\xef\x10\xd6\xc1\xff\xd1\xf7\x11\x67\x83\xe0\xea\x97\x06\xeb\x20\x91\x7c\xd2\xdd\xee\x3c\x08\x37\x6f\x04\x30\x0d\x08\x7c\x34\x8f\xe4\x34\xae\x99\x00\xa7\x7f\x3d\x0d\x4e\xfd\xea\x7e\x22\x7c\xc7\x3a\xfc\x86\x6d\xb0\x6d\xb8\x69\x23\xf5\xb5\xe7\xcc\x6c\x3f\x00\x62\x77\x6e\x54\x68\xae\x57\xad\xf5\x96\x52\x7e\xb0\x6e\x01\xe8\xfb\x66\xd0\x1a\x30\xaf\x95\x83\x28\xcc\x81\x2b\xef\xe6\x92\x3c\x24\x8a\x63\x96\x4d\x71\x7e\xe3\x37\x6c\x83\x6d\xb1\x0f\xf6\x0d\xd6\x15\x48\x5d\x86\x7c\x64\x47\xc2\xc3\xf6\x0a\xae\x66\xc6\xea\x20\xae\xfc\x48\x08\xfc\x55\xdf\x05\x7f\xd5\x77\x00\x22\x41\x08\x7e\xfa\x2a\x74\x6d\x53\xa1\x6e\xf9\x64\x92\x75\x69\xcd\x44\xb8\xb9\x35\x13\x6e\x6d\xcb\x80\xe6\x75\xe3\xa9\x0e\xbf\x61\x1b\x6c\x8b\x7d\xb0\xaf\xbf\xea\x7b\xc4\x8b\xec\xe2\xec\x22\x9c\x83\x66\xc1\xd5\x19\xa9\xe4\xd3\xdc\x1f\x98\x0d\xc1\x33\x6f\x42\x34\xec\x03\x7f\xe5\x1c\x68\xdb\x30\x96\xe4\x9c\xfb\xcd\x7d\xd0\xbd\x63\x0c\xe8\x85\x63\x41\x2f\xbc\x07\x7a\x3d\x6e\xa8\x5f\x39\x89\xbe\x61\x1b\x6c\x8b\x7d\xb0\xaf\xe4\xd9\x65\xd9\x6e\x19\x88\x5d\xd9\xba\xd0\x5c\x73\x53\xca\xd7\xff\x0f\xf4\xfd\x0f\x41\xa8\x61\x35\xf9\x1f\x7d\xdf\x74\x68\x5e\x97\x47\xf3\xdd\xf2\x4e\x1e\xc9\x0d\x9f\xfc\x3b\x30\x4f\xbd\x02\xc6\x9e\x09\xd0\xba\x21\x07\x4e\xfd\x6a\x1a\xb5\xc1\xb6\xe4\xb3\x1a\x56\x13\x0f\xe4\x25\xd7\xed\x39\x5c\x9b\x51\xa1\xb1\x17\xe3\xc8\x7f\x0b\x63\x13\x5c\xef\xd4\xb6\xaf\x91\x7c\x6b\xf8\xf2\x16\x5a\xdb\x62\xcf\x64\xb8\xb4\x7a\x3c\xc9\x68\xdb\x90\x0d\xfe\xb2\x87\x01\x2e\x2d\x07\x68\x5a\x05\x81\xca\x27\xe1\xfa\xe6\x4c\x39\x2f\xab\xc7\x53\xdb\x88\xaf\x9e\xfa\x22\x0f\xe4\xe5\xc4\x47\xa5\x8f\xa2\x0d\xfc\x22\x8e\xfc\x5f\xe0\x37\x3b\x5e\x4f\x2e\xff\x1e\xf0\x97\x7e\x11\xa2\x0d\x4b\x00\x9a\x56\x42\xe0\xe0\x13\x70\x7d\x53\x56\x6a\xf9\x81\x4e\x8a\xbf\x70\xac\x43\xe4\x6b\xec\x45\xd4\x0d\xea\x28\xbe\xfe\x67\x38\xfa\x6f\x5e\x97\x2f\xfd\xcb\xd1\xe7\x21\x74\xec\x87\xa0\xef\xce\x81\xab\xbf\x1d\x67\x7d\xcb\xb3\xd6\xcc\x50\xfd\xe3\xdc\xe2\x1c\xcb\xb3\xd0\x60\xf9\xae\xb9\x68\x1b\xe4\xdf\x53\xd8\x1f\xda\x39\xda\xbe\xee\x55\x41\xf7\xba\xc9\x16\xcf\xaf\xb8\x2f\xa5\xfd\x25\x95\x8f\x6b\x42\x63\xcd\xb8\xcf\x25\x5c\x7f\x5b\xe5\xfa\xc3\x39\xb8\xf0\xdf\xf7\x42\xfb\xc6\x7b\xa0\x63\x53\x36\x5c\x5c\x35\x29\xad\xf5\x97\x42\x7e\x26\xfa\x06\xda\x37\xc2\xbe\xb4\xfc\x0f\xd2\x99\x25\xd3\xd2\xf2\x3f\xc9\xe5\x2b\xf6\x1c\xbc\x86\x3e\x12\x7d\xe5\x50\xff\xeb\x8f\xe3\x7f\x25\x0d\xf5\xbf\xfe\x21\xfe\x37\x99\xfc\x18\x1d\x7c\x09\xf7\x08\xdc\x2b\xc0\xd4\x69\xef\x30\x2a\x46\xb9\xff\x58\x73\x9f\x5a\xbe\xc2\x7c\x1a\xed\x8d\x0b\x70\xaf\xc4\x3d\x53\xee\xbf\xef\x8e\x7a\xff\x4d\x47\x7e\x8c\x0e\xee\xc7\x58\x01\x63\x06\xd4\x1d\xf6\x41\x9b\x18\x51\xfc\x61\xf9\xb2\xb4\xe5\x7b\x55\x1b\xc3\xb3\x18\x33\x61\xec\x84\x31\x14\xf1\xdc\xfb\x40\x5a\xf1\x97\x4e\xf1\xd7\x34\x8a\xd9\x06\x3f\xa9\xe4\x3b\xb6\x48\xb1\xa2\xfa\x12\xc6\x8e\x18\x43\x62\x2c\x89\x31\x25\xc6\x96\x29\xe3\x4f\x6f\x26\xc5\xaa\xf1\x62\x58\x8c\x6d\x31\xc6\x4d\x26\xbf\x7f\x3d\x60\xcc\xac\xce\xa3\x18\x1a\x63\x69\x8c\xa9\xb9\x5b\xc6\xd8\x14\x7f\xe7\xc4\xc4\xdf\x39\xb2\x0e\x63\x73\x8c\x91\x93\xc5\xf0\xc4\x47\x49\x2a\x9f\x30\x68\x0a\xf3\x15\x65\x31\x6b\xaf\x7e\xd3\x3a\x53\xd4\x5b\x67\x8c\x44\xe7\x0f\x49\xc9\xcf\x30\xad\xd6\x59\x27\xa9\xfc\x58\x5d\xf4\x69\x14\x2f\x8d\x95\x67\xab\x51\x9f\xcf\xa6\x5b\x67\xbd\xb4\xe4\x7f\xde\x1f\x7b\x6d\x1c\x66\x2a\x1c\x66\x0c\xe9\x99\xc3\x8c\x4d\xb7\x28\xcf\xa2\x4c\x8b\xd4\x74\xa8\xc5\xa2\x1e\x8b\x02\x16\x99\x8c\xa9\xc0\x48\x90\x6a\xcb\x9d\xc9\x18\x9b\xcd\x18\xfb\xfb\xd8\x3c\xc5\xc3\x9f\xb5\x56\xee\x3e\x77\x9f\x3f\xcc\xc7\xda\x1f\x1f\x16\x5c\x7d\x5b\x70\x75\xa1\xe0\x6a\xc1\xef\x89\x6c\xde\xff\x2a\xb8\xfa\x37\x82\x2b\xb3\x05\x57\x72\x7a\x35\xc6\x0c\x2d\x79\x3e\x29\x0d\xfc\xdf\x16\x5c\x0d\x0a\xae\xc2\x1d\x22\x53\x70\xb5\x53\x70\xf5\x20\xed\xeb\x5c\xc9\xc7\x58\xc3\x48\x33\x3f\x97\x18\xbf\x42\xb1\x57\x5a\x84\x6d\x07\x60\x4a\xd2\x37\x6e\x5b\x97\xfd\x1b\xf5\x56\x2a\xb8\xfa\x24\xec\x67\x4c\xe7\xc3\x1b\x83\x83\x5f\x73\x05\x31\xc6\x0b\xd6\xbe\x45\xe7\xf2\x64\x14\x3c\xbb\x08\x8c\x8a\xa7\xfa\x71\x21\xc6\xc2\x1c\x30\x0e\x7e\x0d\x02\x27\xfe\x81\x78\x20\xe1\x3b\xd6\xc9\x78\x84\x39\xd8\xf5\xe2\x29\x60\x94\xcd\x04\xe1\xcd\x8a\x1d\x47\xa3\xe0\xea\x0b\x7e\xce\x5c\x7a\x8a\xfc\x64\x7c\xfc\x2c\x48\xf1\x75\xb8\x6f\x48\x9c\x37\xf8\x31\xdb\x4a\x64\x9c\x84\xb2\xbd\x19\xe0\x3f\xf2\x17\x60\xb6\xee\x85\x68\xe0\x26\x40\x34\x12\x13\x20\x46\xa8\x0e\xbf\x61\x1b\x6c\x8b\x7d\xf4\xe2\xc9\x10\x6a\x58\x0b\xe1\xcb\x5b\xc1\xa8\xfc\x46\xff\x9c\x70\xb5\x0d\xc7\xd0\xa3\x65\x33\x91\xe6\x3c\x0c\x17\x7f\xa4\xeb\x14\xc5\xb4\xc2\xc3\xe8\x3c\x43\xb1\x65\xb0\x3b\xe5\x98\xb1\x0d\xb6\xc5\x3e\xf2\x7c\x30\x13\x22\xdd\x35\x10\xf5\x5f\xa7\x73\xa9\x28\xbc\xc7\x1e\x43\x93\xe0\xca\x53\x14\x73\x16\xa6\x8e\x89\x86\x83\x9f\xce\x41\x55\xdf\x23\xec\x62\x77\x2e\x9d\xc7\xed\xb3\xa8\x1c\x5c\x10\xa2\xa2\x05\x22\xb7\x3e\x21\xc2\x77\xac\xeb\xff\x1e\xa2\x3e\xd8\x17\x79\x20\xaf\x68\xa8\x87\xda\x84\xea\x97\xcb\xbc\xb3\x1c\x43\xa5\xe0\xea\xe4\x74\x62\xba\xe1\xe0\x0f\x35\xac\x91\x36\xc0\xdd\x10\xac\x7b\xbb\x1f\x7b\x34\x0c\x66\x47\x05\xdd\x3d\xe9\xa5\x8f\x80\x5e\x34\x51\x52\xe9\x23\xf2\x3e\xaa\xa3\x82\xda\xd8\x63\x08\xd6\xfe\x52\xc6\xee\xde\x0c\xe2\x69\x8f\x1d\xcf\x4d\x92\xbf\x1a\x45\x7f\xeb\xf3\x32\x57\x5f\x0a\x3b\x4a\x17\x3f\xea\xd2\x28\x9f\x25\xf5\x76\xe4\x3b\xfd\x79\xad\x70\x1f\x84\xce\xff\x17\xe8\x7b\x26\x59\xfe\xc6\xca\xdb\xdb\x3e\x46\x63\xf4\x0d\xdb\xd8\xbc\xe9\x0c\x78\xe4\x79\xe2\x85\x3c\x69\x9e\x68\x7e\x7b\x21\x50\xfd\x63\x7b\x4d\x5f\x13\x5c\xfd\x7a\xca\x73\x45\x9a\xf8\x69\x7e\xad\xb5\x67\xde\xa8\xea\xd7\x59\xdd\x02\x79\x0f\xc1\xa5\x1f\xe9\xd9\x39\x06\x6e\x6e\xcd\x22\xc2\x77\xb9\x36\x5d\xd4\x06\xdb\xda\xf6\x64\xde\x38\x42\xbc\x90\x27\xf2\x76\x4c\xac\xbb\x86\x72\x44\xd6\x18\xde\xd3\xb9\xcb\x9d\xcc\x1f\xa5\x83\x9f\xf2\x5c\x15\x73\x48\x5f\x81\x4f\x5f\x75\xce\xa8\xe1\xcb\x5b\x1c\x9b\x45\xac\x57\xde\xcd\xa3\x5c\x66\xcd\xd2\x29\x44\xf8\x8e\x75\x72\x1c\x0a\xb5\xc5\x3e\x92\xa9\x49\xbc\x68\x0e\x2a\xe6\x0c\x38\x5b\x87\x2e\x2c\xb1\xce\x86\x6a\xbb\xe0\xea\x57\x93\x9f\x2d\x53\xe3\xa7\x5c\xcf\xae\x6c\xb2\x03\xb3\xb3\x5a\xea\xa9\xaf\x51\x9e\x5d\x35\x06\xdd\xdb\x33\x9c\x9c\x53\x3c\xc2\x6f\xd8\x86\xfc\x4e\xf9\x57\x9c\xfc\x9f\xd9\xf9\xb1\xb4\xbb\xc2\x6c\x30\xdb\xcb\xfb\xf5\xa5\x5f\x95\x79\x41\xb9\x67\x2c\x12\xdc\x9d\xf0\xce\x2a\x1d\xfc\xe4\xdf\xc8\x5f\x7c\x97\x72\xf1\x54\x57\xf3\xef\x64\x17\xbd\x1e\x37\xe5\x6c\x12\x61\xb7\x09\xdb\x60\x5b\xec\x83\x7d\xe5\x00\x0c\xe2\x89\xbc\x51\xc6\x00\x99\x75\x0b\x6d\x1b\x3a\x2e\xb8\x32\x61\xa4\xf8\xf1\x37\xe5\xe9\x35\x06\xa1\x86\x55\x96\x7e\x5a\xc0\x28\x9d\x49\x75\xad\xef\x8d\x1b\x82\xb5\x76\xd9\xc0\x7b\x00\x9b\xb0\xad\xed\xfb\xa3\xfa\x15\x69\x2b\x17\x57\x51\x1d\xca\x88\x95\x8d\xfe\x57\xde\x9f\x29\x3e\xc1\xd5\x6f\x26\xce\x8f\x24\xc7\x1f\xf1\x35\x80\x5e\xf2\x00\xe8\xbb\xf3\x21\x72\xf3\x98\x65\xf7\x1f\x80\xf0\x8e\x81\x9e\x1d\x63\xc8\xc6\x6d\x7c\x35\x4b\xa7\x92\xbd\xdf\xda\x96\x09\x5d\xdb\x33\xa1\x6d\x43\x8e\x73\xbf\x60\xe7\xf9\xb1\x0f\xfa\x48\xdc\x7b\x89\xff\xcd\x6a\xe2\x4d\x79\x65\x5f\xc3\x40\xbd\xfd\xee\x5b\xb6\x0d\x51\x7e\x3b\xde\x3a\x4e\x85\xdf\xec\x38\x44\xb6\x6f\x94\x3d\x46\xff\x67\xc0\x27\x70\xf2\x65\xe2\x7b\xed\xfd\x9c\x98\xbc\xe7\x54\xa9\x5f\x27\x16\x50\x40\xe7\x2a\x5c\xdf\x9c\x4d\xf7\x32\x76\x3b\xec\x83\x7d\x03\x9f\xfc\xa3\xc4\xe9\x6f\x27\xde\x28\x03\x65\x0d\xb0\xa1\x9a\xff\xb0\xf1\x6f\xd3\xb9\x2b\xee\x9d\x69\x2a\xfc\xa4\x6b\x9c\xdf\xc3\xcf\x01\x44\x02\x94\xcb\x35\x2a\x9f\xa6\xba\xa6\xb5\x13\x9c\x7c\x29\xae\x51\x9f\x47\xe6\x93\xf4\xc2\x6c\xd0\x77\x8f\x73\xe2\xcd\xcb\xeb\xf3\x9d\xfc\x2a\xf6\x21\x7e\x87\x9e\x96\x79\xe1\x48\x80\x78\x63\x1d\xca\x1a\x20\xfb\x2a\xb7\xf7\xb3\x6a\xc1\x95\xdc\x91\xe0\x0f\x5d\x58\x26\xfd\xe6\xf1\x97\xa4\xbe\xc4\x65\xca\x5d\xf5\xee\x54\x29\xe7\x6d\xeb\xb5\x0d\xf5\xca\x5d\x60\xec\x9d\x4a\x77\x30\x91\xda\x37\xc0\x5f\x3e\x93\xc6\xd3\xf9\xc1\x58\xb2\x2d\x3b\x4f\xee\xdb\xa9\x12\x0f\xe4\x45\xf3\x79\xfc\x25\x92\x81\xb2\x06\xd8\xee\xcd\x13\xf2\xce\x98\x2b\x4d\x82\xab\x0f\x26\xc9\x91\x3e\x97\x08\x3f\xed\x4f\x3b\x19\x04\x4f\xbd\x2e\x79\xf6\xd4\xd2\xbe\xd3\xbd\x7d\x0c\x9c\xb5\x6c\x1b\xb1\xe1\x7e\xa5\x7b\x55\x08\x1d\xfb\x11\x40\xf3\x5a\x22\xf3\xf4\xcf\x69\x2e\xd0\xff\xa3\xed\x23\x7e\xec\xd3\xbd\xdd\x4d\x31\x74\xa4\xa7\x4e\xca\x38\xf5\xba\x94\x81\xfb\x5b\xac\xef\x10\xcd\x74\xf7\x64\xdd\x79\xcf\x4a\x82\xff\x5b\x42\x63\x86\x71\xf0\xc9\x21\xb1\x24\xf9\x4e\xe4\x5d\x3b\xdf\xd2\xc9\x71\xd0\x8b\xf2\xa1\x6b\x5b\x86\xe3\x63\xb0\xec\xda\x9e\x41\xf7\x6f\x66\xcd\xeb\x00\xcd\x6b\x00\x9a\x56\x43\xb4\x7e\x31\x18\x7b\x27\x83\xcf\x23\xe7\x0a\xf1\x3b\x6d\x8b\xf2\xe9\xff\x3a\x24\xa3\x76\xbe\x94\x31\xc8\x87\x62\x6c\x4a\x31\x8b\xc6\xfa\x04\x57\x9f\x4e\x82\xff\x69\x6c\x43\xb1\x88\xff\xfa\xc8\xf0\x6f\xcb\x00\x7d\x57\x16\x98\x67\x7e\xde\x8f\xff\x42\x01\x18\xc5\x93\x08\xff\xf9\x91\xe0\x0f\x76\x03\xea\x14\x75\x4b\x3a\x4e\x8c\x7f\x16\xe5\x96\xf7\xcd\xa0\x39\x4b\x6e\x3f\x75\x34\xf7\x68\x03\xb6\xfd\xa0\xef\xb9\xb1\x85\xee\xd3\x21\x78\xe4\xcf\xe8\x0e\x10\xed\x27\x7c\xe2\x45\xd0\xbd\x19\xd0\xbd\x23\x83\xda\x0e\xdb\x7e\xc2\x7d\x74\x67\x81\xb6\x4d\x36\x9e\x18\xff\x83\xb8\x46\xf4\xa2\x09\x74\xdf\x31\x92\xf5\x7b\xf5\xb7\xb9\x96\xef\xc9\x81\x60\xd5\x73\x10\xaa\xfe\x3e\xe8\xc5\xf7\x82\xce\x15\xba\xd3\xc2\x31\x9e\xfa\xb5\xbd\x7e\x95\xb4\xd6\xef\x20\xfc\xdf\x8e\x8f\x9f\x72\xeb\xb9\xe4\xa3\x70\x5f\xb9\xca\x07\xf0\x48\xd7\x7f\x9e\x5b\x81\x7b\x53\x46\x7f\xec\x6c\x91\x4f\xeb\x8f\x2f\xb0\x6d\xe3\x30\xfc\x67\xba\xf8\xe5\xde\xa0\x6e\x47\x1e\xc1\x9a\xf9\x03\x78\xa4\xbb\x7f\x21\x35\xad\x1b\x0f\xbd\x9e\x31\xd6\x3d\xa6\x0a\x7d\x9a\x9b\xe6\x05\x75\x3f\x64\xff\x3a\xf9\xb2\xb5\x46\x13\xef\x5f\xe9\xe0\xd7\xed\xbb\x3e\xdc\xa3\x51\x2f\xbf\xfb\xd3\x81\x71\x48\xdc\xf8\x61\x6b\xdc\xf8\xc1\xde\xc7\x30\x6e\x40\x9c\xa8\xeb\x58\xec\xc3\x89\x1f\xd2\xc5\x1f\xb3\x06\xbe\x29\xb8\xd2\xa7\xef\xb9\x0f\x22\x5d\x9f\x0e\xe0\xe1\xc4\x6f\x17\xd3\x8b\xdf\x12\xd1\x70\xe2\xb7\xe1\xe1\xa7\x35\x30\x81\x62\x55\xcd\x45\xe7\xd0\xd8\xe7\xb3\x88\x9f\x87\x85\x5f\x53\x18\xfd\x3f\x95\xab\x8b\xe5\x19\xe3\x71\x3a\x43\x38\x6b\xe0\xf7\x76\x7e\xa9\x8e\x7b\x7e\x19\x2e\xfe\x18\x1b\xc2\xb3\x5a\x07\x9e\xdd\xf0\x0c\xe7\xf0\xb9\x23\xe7\xc7\x9b\xa3\xc4\x4f\x7e\xc8\x8d\x67\x66\x3a\xa7\xef\x7f\x88\xce\xd2\xf6\x93\xf6\xf9\x7d\x47\xcc\xf9\x7d\x47\xb2\xf3\x7b\x55\xdc\xf3\xfb\x48\xf1\xc7\xcc\xc1\xd7\x28\x77\x81\x3e\xae\xfa\xaf\x29\xa7\x01\x43\xf2\x27\xcf\x0f\xcc\x9f\x9c\xbb\x3d\xf9\x93\xd1\xe2\xef\xe3\x2e\xe6\xe3\x4c\x91\x67\x66\x35\x8a\x7e\x8e\xd6\xb2\xa5\xb3\xd4\xf9\xab\x17\xc1\x28\x7d\x04\xf4\x3d\x13\x89\x8c\x44\xf9\xab\xba\xb7\x87\xe6\xaf\x6e\x03\xfe\x98\x39\x98\x2c\xb8\x7a\xc8\xb6\xd9\x50\xfd\x32\x99\x1b\x0c\xf5\x38\xfe\xe2\xf6\xe5\x0f\x13\xe7\x4e\x47\x84\xbf\x50\xb5\xfd\xe9\x1c\x2b\x97\x4a\xb9\x55\xfa\xdf\x50\xe0\x3a\x44\xba\xcf\xc8\xdc\x37\xda\x8a\x9d\xbf\x4d\x82\xc1\xc1\x62\xe5\x6f\xb1\x0f\xe5\x7e\x4b\x1f\xa5\x5c\x70\xd2\x3e\x23\xc0\x4f\x63\xf0\xba\x58\x8f\x27\x1b\xe7\xe1\x05\x99\xd3\x56\x68\xbe\x8d\xca\x6f\xd0\xbe\x19\x6a\x58\xeb\xac\x3d\x27\x7f\xde\x56\x92\x7e\xfe\xbc\xe4\x41\x30\xdb\xf6\xa5\x1e\xf3\x08\xf1\xe3\xa3\x7b\x15\x66\xec\xa2\xff\x6d\xbc\x60\xdd\x2d\x48\xbc\xbb\xb2\xe8\xbf\x31\x7a\xf1\xd4\x24\xf7\x17\xf3\x89\x12\xdd\x5f\x18\x15\x4f\xd1\x1d\x48\xca\x7b\x92\xda\xb7\xac\xff\xda\xba\x86\x8d\x9f\xc6\xc0\x5d\x0c\xbc\xe4\x5b\xd1\x27\x95\x39\x77\x64\x8e\x8f\x19\xcd\xfd\xd1\xb0\xee\xa9\x46\x84\x1f\x1f\x43\x73\xd9\x6b\x7a\xbc\xbc\x6b\xa3\x3b\xb7\x4e\xeb\x0e\xee\x4e\xdd\xf7\x8d\x18\xbf\x33\x0e\x8f\xc2\x7a\x8b\x54\x8c\x35\x72\x04\x57\x67\x0b\xae\xfe\xc4\xba\x0b\x2d\xb8\x03\xf7\xae\x6f\x5b\x77\xbc\x23\xc6\x7f\xf7\xb9\xfb\x7c\x9e\x1e\xb9\x43\x24\x2e\x5b\x18\x63\xcf\x58\x65\x9e\x55\x66\x5a\xa5\x6b\x50\xc9\xec\xb2\xc0\x2a\x9f\x19\x54\x4e\x4f\x50\xe6\x25\x28\x33\x6f\x5f\xd9\x93\xa0\x0c\x24\x28\xcd\x41\x65\xd4\x2a\xc1\x2e\x17\x0e\x2a\x5b\xac\xb2\xc7\x2a\x4d\xab\x4c\xa1\xdf\xff\x0f\x00\x00\xff\xff\xc6\xb9\x24\x2f\xee\x3a\x00\x00") - -func faviconIcoBytes() ([]byte, error) { - return bindataRead( - _faviconIco, - "favicon.ico", - ) -} - -func faviconIco() (*asset, error) { - bytes, err := faviconIcoBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "favicon.ico", size: 15086, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _jsJquery211Js = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xce\xcf\x2b\xce\xcf\x49\xd5\xcb\xc9\x4f\xd7\x50\x4a\xad\x48\xcc\x2d\xc8\x49\x55\xd2\xb4\x06\x04\x00\x00\xff\xff\xc8\x9f\xbd\x5f\x17\x00\x00\x00") - -func jsJquery211JsBytes() ([]byte, error) { - return bindataRead( - _jsJquery211Js, - "js/jquery-2.1.1.js", - ) -} - -func jsJquery211Js() (*asset, error) { - bytes, err := jsJquery211JsBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "js/jquery-2.1.1.js", size: 23, mode: os.FileMode(438), modTime: time.Unix(1591451173, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "css/bootstrap.min.css": cssBootstrapMinCss, - "favicon.ico": faviconIco, - "js/jquery-2.1.1.js": jsJquery211Js, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"} -// AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("notexist") would return an error -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "css": {nil, map[string]*bintree{ - "bootstrap.min.css": {cssBootstrapMinCss, map[string]*bintree{}}, - }}, - "favicon.ico": {faviconIco, map[string]*bintree{}}, - "js": {nil, map[string]*bintree{ - "jquery-2.1.1.js": {jsJquery211Js, map[string]*bintree{}}, - }}, -}} - -// RestoreAsset restores an asset under the given directory -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil -} - -// RestoreAssets restores an asset under the given directory recursively -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) -} diff --git a/_examples/file-server/embedding-files-into-app/main.go b/_examples/file-server/embedding-files-into-app/main.go deleted file mode 100644 index ccb39b2547..0000000000 --- a/_examples/file-server/embedding-files-into-app/main.go +++ /dev/null @@ -1,43 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -// Follow these steps first: -// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata -// $ go-bindata -prefix "assets" -fs ./assets/... -// $ go run . -// "physical" files are not used, you can delete the "assets" folder and run the example. -// -// See `file-server/embedding-gzipped-files-into-app` example as well. -func newApp() *iris.Application { - app := iris.New() - app.Logger().SetLevel("debug") - - app.HandleDir("/static", AssetFile()) - - /* - Or if you need to cache them inside the memory (requires the assets folder - to be located near the executable program): - app.HandleDir("/static", http.Dir("./assets"), iris.DirOptions{ - IndexName: "index.html", - Cache: iris.DirCacheOptions{ - Enable: true, - Encodings: []string{"gzip"}, - CompressIgnore: iris.MatchImagesAssets, - CompressMinSize: 30 * iris.B, - }, - }) - */ - return app -} - -func main() { - app := newApp() - - // http://localhost:8080/static/css/bootstrap.min.css - // http://localhost:8080/static/js/jquery-2.1.1.js - // http://localhost:8080/static/favicon.ico - app.Listen(":8080") -} diff --git a/_examples/file-server/embedding-files-into-app/main_test.go b/_examples/file-server/embedding-files-into-app/main_test.go deleted file mode 100644 index fd8116bb34..0000000000 --- a/_examples/file-server/embedding-files-into-app/main_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package main - -import ( - "io/ioutil" - "path/filepath" - "runtime" - "strings" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -type resource string - -// content types that are used in the ./assets, -// we could use the detectContentType that iris do but it's better -// to do it manually so we can test if that returns the correct result on embedding files. -func (r resource) contentType() string { - switch filepath.Ext(r.String()) { - case ".js": - return "text/javascript" - case ".css": - return "text/css" - case ".ico": - return "image/x-icon" - case ".html": - return "text/html" - default: - return "text/plain" - } -} - -func (r resource) String() string { - return string(r) -} - -func (r resource) strip(strip string) string { - s := r.String() - return strings.TrimPrefix(s, strip) -} - -func (r resource) loadFromBase(dir string) string { - filename := r.String() - - filename = r.strip("/static") - - fullpath := filepath.Join(dir, filename) - - b, err := ioutil.ReadFile(fullpath) - if err != nil { - panic(fullpath + " failed with error: " + err.Error()) - } - - result := string(b) - - if runtime.GOOS != "windows" { - result = strings.Replace(result, "\n", "\r\n", -1) - result = strings.Replace(result, "\r\r", "", -1) - } - return result -} - -var urls = []resource{ - "/static/css/bootstrap.min.css", - "/static/js/jquery-2.1.1.js", - "/static/favicon.ico", -} - -// if bindata's values matches with the assets/... contents -// and secondly if the HandleDir had successfully registered -// the routes and gave the correct response. -func TestEmbeddingFilesIntoApp(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - route := app.GetRouteReadOnly("GET/static/{file:path}") - if route == nil { - t.Fatalf("expected a route to serve embedded files") - } - - if runtime.GOOS != "windows" { - // remove the embedded static favicon for !windows, - // it should be built for unix-specific in order to be work - urls = urls[0 : len(urls)-1] - } - - for _, u := range urls { - url := u.String() - contents := u.loadFromBase("./assets") - - e.GET(url).Expect(). - Status(httptest.StatusOK). - ContentType(u.contentType(), app.ConfigurationReadOnly().GetCharset()). - Body().Equal(contents) - } -} diff --git a/_examples/file-server/embedding-gzipped-files-into-app/bindata.go b/_examples/file-server/embedding-gzipped-files-into-app/bindata.go deleted file mode 100644 index 6a38528513..0000000000 --- a/_examples/file-server/embedding-gzipped-files-into-app/bindata.go +++ /dev/null @@ -1,382 +0,0 @@ -// Code generated by go-bindata. (@generated) DO NOT EDIT. - -//Package main generated by go-bindata.// sources: -// ../embedding-files-into-app/assets/css/bootstrap.min.css -// ../embedding-files-into-app/assets/favicon.ico -// ../embedding-files-into-app/assets/js/jquery-2.1.1.js -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data []byte, name string) ([]byte, error) { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -// Name return file name -func (fi bindataFileInfo) Name() string { - return fi.name -} - -// Size return file size -func (fi bindataFileInfo) Size() int64 { - return fi.size -} - -// Mode return file mode -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} - -// ModTime return file modify time -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} - -// IsDir return file whether a directory -func (fi bindataFileInfo) IsDir() bool { - return fi.mode&os.ModeDir != 0 -} - -// Sys return file is sys mode -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -type assetFile struct { - *bytes.Reader - name string - childInfos []os.FileInfo - childInfoOffset int -} - -type assetOperator struct{} - -// Open implement http.FileSystem interface -func (f *assetOperator) Open(name string) (http.File, error) { - var err error - if len(name) > 0 && name[0] == '/' { - name = name[1:] - } - content, err := Asset(name) - if err == nil { - return &assetFile{name: name, Reader: bytes.NewReader(content)}, nil - } - children, err := AssetDir(name) - if err == nil { - childInfos := make([]os.FileInfo, 0, len(children)) - for _, child := range children { - childPath := filepath.Join(name, child) - info, errInfo := AssetInfo(filepath.Join(name, child)) - if errInfo == nil { - childInfos = append(childInfos, info) - } else { - childInfos = append(childInfos, newDirFileInfo(childPath)) - } - } - return &assetFile{name: name, childInfos: childInfos}, nil - } else { - // If the error is not found, return an error that will - // result in a 404 error. Otherwise the server returns - // a 500 error for files not found. - if strings.Contains(err.Error(), "not found") { - return nil, os.ErrNotExist - } - return nil, err - } -} - -// Close no need do anything -func (f *assetFile) Close() error { - return nil -} - -// Readdir read dir's children file info -func (f *assetFile) Readdir(count int) ([]os.FileInfo, error) { - if len(f.childInfos) == 0 { - return nil, os.ErrNotExist - } - if count <= 0 { - return f.childInfos, nil - } - if f.childInfoOffset+count > len(f.childInfos) { - count = len(f.childInfos) - f.childInfoOffset - } - offset := f.childInfoOffset - f.childInfoOffset += count - return f.childInfos[offset : offset+count], nil -} - -// Stat read file info from asset item -func (f *assetFile) Stat() (os.FileInfo, error) { - if len(f.childInfos) != 0 { - return newDirFileInfo(f.name), nil - } - return AssetInfo(f.name) -} - -// newDirFileInfo return default dir file info -func newDirFileInfo(name string) os.FileInfo { - return &bindataFileInfo{ - name: name, - size: 0, - mode: os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir - modTime: time.Time{}} -} - -// AssetFile return a http.FileSystem instance that data backend by asset -func AssetFile() http.FileSystem { - return &assetOperator{} -} - -var _cssBootstrapMinCss = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xbd\xef\x8f\xe3\x38\x92\x20\xfa\xb9\x1a\xe8\xff\x41\x5b\x8d\x41\x57\x4d\x59\x2e\xf9\x77\xda\x89\xce\xb7\xfb\xe6\x0e\xb7\x03\xdc\xec\x97\x9b\x0f\x07\xf4\xf4\x7b\xa0\x25\xda\xd6\x94\x2c\x69\x24\x39\x2b\xb3\xfb\xf6\xfe\xf6\x83\xf8\x4b\x41\x32\x48\xd1\x4e\x77\xcf\xdc\x62\xb7\xee\x7a\x9c\x62\x44\x30\x18\x0c\x06\x83\x41\x32\xf8\xf9\xf7\xff\xf4\xed\x37\xd1\xef\xa3\xff\xb7\xaa\xba\xb6\x6b\x48\x1d\x3d\x2f\xa6\x8b\xe9\x32\xfa\x70\xea\xba\x7a\xf7\xf9\xf3\x91\x76\x7b\x59\x36\x4d\xab\xf3\x47\x06\xfe\x87\xaa\x7e\x6d\xf2\xe3\xa9\x8b\xe6\xc9\x6c\x16\xcf\x93\xd9\x2a\xfa\xf3\xd7\xbc\xeb\x68\x33\x89\xfe\x58\xa6\x53\x06\xf5\xdf\xf3\x94\x96\x2d\xcd\xa2\x4b\x99\xd1\x26\xfa\xd3\x1f\xff\xcc\xc9\xb6\x3d\xdd\xbc\x3b\x5d\xf6\x3d\xc5\xcf\xdd\xd7\x7d\xfb\x59\x55\xf2\x79\x5f\x54\xfb\xcf\x67\xd2\x76\xb4\xf9\xfc\xdf\xff\xf8\x87\xff\xfa\x6f\xff\xe3\xbf\xb2\x4a\x3f\x47\x9f\x7f\xff\x4f\x51\x59\x35\x67\x52\xe4\x3f\xd3\x69\xda\xb6\x3d\xb3\xc9\x74\x1e\xfd\x2f\x46\x5b\x54\x17\xfd\xaf\xe8\x98\x77\xd3\xbc\xfa\xac\x60\xa3\xdf\x7f\xfe\xf6\x9b\x53\x77\x2e\xa2\x5f\xbe\xfd\xe6\xdd\xa1\x2a\xbb\xf8\x40\xce\x79\xf1\xba\x8b\x5a\x52\xb6\x71\x4b\x9b\xfc\xf0\xf8\xed\x37\xef\xe2\xaf\x74\xff\x25\xef\xe2\x8e\xbe\x74\x71\x9b\xff\x4c\x63\x92\xfd\xf5\xd2\x76\xbb\x68\x96\x24\xbf\x63\x10\xe7\xd6\x51\xfa\xed\x37\xff\xfe\xed\x37\xdf\x7e\xb3\xaf\xb2\x57\x56\xcd\x99\x34\xc7\xbc\xdc\x45\x89\x28\x20\x4d\x97\xa7\x05\x9d\x44\xa4\xcd\x33\x3a\x89\x32\xda\x91\xbc\x68\x27\xd1\x21\x3f\xa6\xa4\xee\xf2\xaa\x64\xbf\x2f\x0d\x9d\x44\x87\xaa\x62\xb2\x3c\x51\x92\xb1\xff\x3d\x36\xd5\xa5\x9e\x30\xb2\x79\x39\x89\xce\xb4\xbc\x4c\xa2\x92\x3c\x4f\xa2\x96\xa6\x1c\xb7\xbd\x9c\xcf\xa4\xe1\x95\x67\x79\x5b\x17\xe4\x75\x17\xed\x8b\x2a\xfd\x22\x39\xb8\x64\x79\x35\x89\x52\x52\x3e\x93\x76\x12\xd5\x4d\x75\x6c\x68\xdb\x4e\xa2\xe7\x3c\xa3\x95\x8e\x97\x97\x45\x5e\xd2\x98\xa1\xf7\xed\x7e\xa6\x3d\xfb\xa4\x88\x49\x91\x1f\xcb\x5d\xb4\x27\x2d\xed\x21\x20\xe9\x5d\x59\x75\xd1\x87\x1f\xd3\xaa\xec\x9a\xaa\x68\x7f\x8a\x3e\x6a\x24\xcb\xaa\xa4\x3d\xa9\x13\xed\x35\x67\x10\xcc\x8f\xa7\x3c\xcb\x68\xf9\xd3\x24\xea\xe8\xb9\x2e\x48\x47\x23\x0b\x4f\x56\xc3\x4a\xf6\x24\xfd\xd2\xcb\xa3\xcc\xe2\xb4\x2a\xaa\x66\x17\x75\x0d\x29\xdb\x9a\x34\xb4\xec\x24\xe4\x8e\xa4\x5d\xfe\xdc\x8b\x7b\x77\xaa\x9e\x69\xc3\x30\xab\x4b\xd7\x33\x0d\x3a\x65\xbf\x6f\x7e\xec\xf2\xae\xa0\x3f\x71\xd2\x55\x93\xd1\x26\xde\x57\x5d\x57\x9d\x77\xd1\xac\x7e\x89\xb2\xaa\xeb\x68\x26\x7b\x77\x12\xb5\x5d\x53\x95\xc7\x41\x93\xbe\x8a\xe6\x6c\x12\x49\x34\x3b\x94\x43\x71\xdb\xbd\x16\x74\x17\xe5\x1d\x29\xf2\x54\x00\x9c\x66\x9a\x86\x4c\xd7\x1b\x7a\x8e\x92\x47\x85\x92\xff\x4c\x77\xd1\x9c\x9e\x05\xf8\x99\x34\x5f\x18\x82\x68\xed\x77\x49\xc2\x80\x07\x39\xec\xa2\xef\x0e\x07\x59\x7d\x7b\x26\x05\xd0\x74\x4e\xed\x41\x29\x68\x7b\xe9\x1b\x71\xa9\x19\x44\x5d\xb5\x79\xaf\x3d\xbb\xa8\xa1\x05\xe9\x05\x66\x70\xb1\x59\x31\xb5\x67\xca\xa0\x3a\x2e\x40\x21\x64\x05\x5d\x55\xef\xa2\x78\xba\x52\x8d\x69\x2f\x7b\x21\x69\x2e\xe2\x78\x3a\x1f\x0a\xf3\xf3\x11\x74\xc3\xd0\x4d\xed\xf3\x91\x2b\xd7\xae\xa9\xaa\x8e\xeb\x55\xdf\xa9\x87\xa2\xfa\xba\x8b\xb8\xfe\x08\x50\x3e\x82\x34\xf9\xce\xe8\x39\x5a\x26\xf5\x8b\x94\x3e\xd7\x05\xad\x35\x72\xe0\xef\xab\x97\xbe\xe1\x79\x79\xdc\x45\xbd\x1e\xd3\x92\x7d\xe3\x23\xbf\xfa\xd9\x57\xee\x28\x12\x95\xd6\x82\xa7\x81\x6b\x72\xe9\x2a\x51\x98\x56\xbd\x41\xf8\xb2\xcf\xfa\x41\x49\x27\x51\x4b\xce\xb5\x6d\xaa\xce\x55\x59\xb5\x35\x49\xe9\x64\xf8\x69\xf4\xd6\x4c\x49\x72\x7f\xe9\xba\xde\x28\xe4\x65\x7d\xe9\x26\x51\x55\x77\xdc\x82\x44\x2d\x2d\x68\xda\xf5\x63\xed\xa5\x23\x0d\x25\xba\xad\x92\xf4\x7a\x03\x70\xa2\x4d\xde\x3d\x0e\x6a\x27\xbe\x68\x15\x18\x6d\x7a\xce\xdb\x7c\x5f\x50\x83\x07\x5e\x25\x57\x87\xde\x74\xb2\xd1\x7a\xa8\x9a\xb3\x36\xb6\x25\x34\xb3\xd3\x8c\xed\x1f\xbb\xd7\x9a\xfe\xc0\xbf\xff\x34\x81\xdf\x1a\xda\xd2\x4e\xff\xd4\x5e\xf6\xe7\xbc\xe3\xa3\x58\xf6\x26\xa9\x6b\x4a\x1a\x52\xa6\x74\x17\x71\x32\xac\x39\x97\xa6\xed\xdb\x53\x57\x79\xd9\xd1\x46\xab\xfe\xc7\x2c\x6f\xc9\xbe\xa0\xd9\x4f\x1a\x23\xea\x2b\x1f\x86\x82\x40\x46\x0f\xe4\x52\xe8\x02\xd9\xed\x98\x9e\x1c\xaa\xf4\xd2\xc6\x79\x59\xf6\xc6\x9b\xd1\xb0\x0b\xf8\x00\x24\x59\xc6\x54\x86\x8f\x68\x43\xef\x19\x26\x83\xd3\x06\x20\x9f\xd8\x20\x0c\x97\x41\x7a\xa2\xe9\x97\x7d\xf5\x62\x08\x8b\x64\x79\xa5\x0b\x06\xea\xaa\x32\x79\xb8\x96\xeb\xc5\xee\x92\xa1\x21\x36\x5f\xe5\xe5\xbc\xa7\xcd\x4f\xbb\x9d\xac\x9f\xb5\x3f\x6e\xeb\xbc\x8c\x35\x45\x75\x80\x57\x97\x4e\x07\xff\xf6\x9b\x77\x70\x08\x83\xa1\x04\x35\x82\x92\x26\x3d\xb9\x1b\x7e\x9f\xf1\xfd\xe8\xd0\xb7\x5e\xd3\x0f\x39\x2d\x32\x27\x63\x43\xfb\xf8\x87\x38\xed\x31\x0b\x4c\x22\x2e\x8c\x8c\xa6\x55\x43\x7a\x03\x2e\x24\x82\x71\x02\xc6\x18\x63\xa8\xa5\x9d\xae\x7a\xd3\xc5\x8a\x9e\xa3\xe9\x7a\xce\xfe\x67\xb3\xa2\xe7\x47\x68\x13\xa2\x79\xfd\x02\x95\xb3\x9f\x14\xdb\xaa\xc8\xb3\xa8\xcd\x8b\x67\x35\x80\x0a\x7a\xa4\x65\x16\xa0\xd4\x9a\xe5\x41\xed\xa1\xb4\x56\xbe\x49\xb6\xeb\x07\x24\x9c\xb3\x7b\x7b\x68\x56\xda\xfb\x07\x05\xa9\x5b\xda\x77\x19\xff\x25\xd1\xb3\x49\xd4\x9d\x0c\x6e\x59\xd9\xbb\xde\xcd\xfc\x1f\xd5\xa5\xe9\x65\x87\xb8\xab\xa7\xd5\xbe\xfe\xdc\xdb\x86\x55\xbc\xaf\xf2\x82\x36\xcc\x65\xd1\xdc\xd6\xb6\x49\x3f\xa7\x6d\xfb\xb9\xf7\xd5\x98\x9f\xda\xfb\x9f\xff\x7c\xa6\x59\x4e\xa2\xba\xc9\x4b\x2e\xff\xdf\x4f\xa2\x1d\x39\x30\x37\x6f\xb7\xa7\x87\x4a\xcc\x10\x70\x96\x8f\xfe\x29\x3f\xd7\x55\xd3\x91\x92\x19\x62\x6e\x3e\xdb\x13\xc9\x7a\x81\xf5\xfd\x6a\x02\x40\x97\x20\x89\x2c\x7c\x6d\x18\xf8\xc8\xb8\xcb\xbf\xfd\xe6\x5d\x2f\x24\xd2\x3b\x56\xbd\xb9\xef\x28\xef\x73\xce\xdb\xa0\x90\x3b\xee\xf5\x73\x97\x80\xa3\xfc\x78\x6a\xe8\xe1\x27\xde\x66\xd9\x54\x36\x8e\x76\xd1\xfb\xe8\xc3\xfb\x88\x74\x5d\xf3\xa1\x87\xf9\x18\xbd\xff\xf8\x5e\x62\x0d\x1e\xda\x08\x26\x03\xd2\x50\x59\x85\xff\xdf\x0f\xef\xff\x4a\x9e\x49\x9b\x36\x79\xdd\xed\xde\xff\x24\x65\xae\x4a\xbf\x7b\xef\xa0\x2c\xe9\x30\x27\xf8\x6f\x97\xaa\xa3\x6c\x7e\xe6\x60\xf6\x68\xf8\x6e\xbb\xdd\x32\xe9\xd5\xe4\x48\xe3\x7d\x43\xc9\x97\x38\x2f\x7b\x67\x7f\x17\x91\xe7\x2a\xcf\x04\xb9\xae\x77\xea\x39\x11\xe5\xe3\x32\x6d\x8e\xb9\xb7\x1f\x33\xdd\x17\xc0\xf9\xf9\x38\x89\x3a\xc1\xda\x08\x61\xe9\x3d\xbd\x3b\x93\x97\xf8\x6b\x9e\x75\x27\xbe\x32\xb1\x7b\xef\x34\x9f\x44\xa7\xc5\x24\xe2\x23\xec\x5d\xd5\xd4\x27\x52\xb6\xbb\x68\xc1\xf8\xff\x9a\x67\xd5\xd7\xfe\x2f\x0d\xda\x62\x81\xc9\x4c\xe7\x00\xcc\xf4\xa6\x77\x7a\xb0\xb9\x98\x96\xe4\x79\x4f\x1a\x43\x14\xdc\x5c\x71\x80\x7d\x57\x3e\x4d\x53\xd2\xd0\x6e\x12\x4d\xb3\xa6\xaa\x2f\xf5\x13\xf8\x08\x7b\x22\xee\xaa\x3a\xc6\x87\x8e\xa4\x56\x90\x3d\x2d\x9c\xbd\x97\xf4\xa6\x85\x03\x0e\xb6\xc5\x6d\x47\x10\xfa\x1c\xad\xb7\x2c\xf2\xe7\xc9\x14\x85\xe2\x10\x17\x08\x57\x03\x5e\x27\xcd\x00\x29\xf0\xed\xe4\x6c\x41\x96\x65\x16\x4d\x66\xec\xfe\x59\xf8\x91\x29\xb5\xbd\xca\xef\xff\x5b\xf1\x5a\x9f\xf2\xb4\x2a\xdb\xe8\x5f\x49\x71\x28\xf2\xf2\xd8\x7e\xdf\xeb\x41\xdb\xa4\xbb\xe8\xd2\x14\x1f\xa6\xd3\xcf\x3d\x4a\xfb\xf9\xa8\x40\xe3\x93\x04\x8d\x1b\x7a\xbc\x14\xa4\x99\xd2\xaa\xfb\x78\x1b\xda\xff\xf3\x5d\x4e\x0f\xf9\xcb\xc7\xbe\x59\xbd\x5b\x48\xba\x0f\xdf\xd3\xf3\x9e\x66\x19\xcd\xe2\xaa\xa6\x65\x3f\x07\x7e\xff\xb1\x5f\xfd\xbe\x0b\x27\xfc\xb5\x3a\x1c\xe6\x1f\x23\x49\x90\xfd\x79\x13\x11\x9d\xc6\xd5\x24\xba\x0e\x50\xe8\x9a\x0b\xbd\xa9\x35\xed\xf3\xf1\xbb\x01\xe0\xff\x57\x00\xa2\x5c\x93\x5d\xfb\x7c\xfc\xfe\xa3\xe8\xfa\xa9\x42\xf2\xac\xf7\xd8\x22\x6d\xc6\x67\x79\x67\x04\x20\x50\x6b\xe0\xa2\x97\xfb\xa9\x8f\xe6\x24\xbe\xe4\xcb\x57\xcd\xa5\x9d\x41\x3f\x8a\xd3\x38\x57\x55\x77\x62\x13\x33\x29\xbb\x9c\x14\x39\x69\x69\xa6\x3c\xb5\xaa\x7d\xb1\xe0\x8e\x0d\x79\x6d\x53\xa2\x16\x20\x43\xe3\x63\x36\x31\xe7\xed\x17\x38\xd3\x0e\x96\xfe\x2f\x73\xf2\xde\xc6\xa9\x8b\x4b\xeb\x82\xdf\x23\xf0\xf4\xd2\x08\xf0\x49\xa4\x7f\xae\x5c\x64\x12\x92\x22\x84\xce\x79\xe9\xae\x79\x3e\x9b\x23\x28\x69\x51\x5d\x32\x17\xca\x3a\x99\x61\xec\x96\xcf\xb4\xa8\x6a\xea\xc2\xda\x24\x5b\x4c\x28\xb4\x4c\xf3\xc2\x8d\x73\x40\x70\x8e\x05\x69\x5d\xed\xa1\x09\xca\xdc\xf9\xd2\xe6\xa9\x1b\x05\x13\x01\xf7\x89\xdd\x38\x0b\x04\xe7\x44\x49\xd3\xb9\x51\x56\x58\x35\x1d\x69\xdc\x18\x6b\x07\x46\x4c\xcf\x75\xf7\xea\xc6\xdb\x20\x78\x97\x96\x7a\x6a\x7a\x40\x30\x0e\x79\x71\x76\x63\x60\xdd\xd9\x9d\xe2\x82\x34\x47\x97\x12\xd0\x64\x96\xa0\x58\x6e\x78\xac\x37\xfb\x5a\xf2\xd6\x2d\x68\x54\xa5\x2b\xd7\x60\xa5\xc9\x0c\xeb\xcb\x86\x9e\xab\x67\x4f\x43\x96\x08\xce\xcf\x55\x75\x8e\xf3\xd2\x8d\x84\x69\x00\x43\xaa\x2e\x9e\xe6\x60\x5a\x50\x1d\x0e\x6e\x04\xac\xfb\xdb\xfc\x58\x12\xd7\x48\xa3\xc9\x0c\x53\x80\xb4\x3a\xba\x11\xd0\xfe\x6f\x48\xeb\xee\xcc\x39\xd6\xf9\xa7\xea\xec\x96\xf2\x1c\xeb\xfe\x43\x5e\x78\x30\xb0\xbe\xef\x72\x5f\x1d\x68\xef\x57\xc4\x65\xff\x68\x32\xc7\xfa\x3e\xab\xbe\x96\x45\x45\xb2\x98\x14\xee\xae\x9c\x63\x0a\x20\x31\xdd\x58\x98\x02\x5c\x6a\x3f\x0e\xa6\x03\x79\xb9\xaf\x5e\xdc\x28\x98\x0a\xf4\xb3\x77\x9c\xe6\x4d\xea\x93\x39\xa6\x0a\x0d\xad\x29\x71\x4b\x62\x81\xe9\x42\x43\x0f\x0d\xf5\x28\xd0\x02\x53\x87\xde\x14\x78\x85\xbe\xc0\x54\xa2\xf7\x43\xdc\x18\x98\x4a\x1c\x0a\xe2\x1e\x0d\x0b\x4c\x25\xfa\x05\x58\x7d\xaa\x4a\xea\x9e\xad\x16\x98\x42\x3c\x57\xc5\xe5\x4c\xbd\x43\x7c\x81\xa9\x84\xc0\xeb\xf5\xc9\x8d\x88\xe9\x85\x40\xbc\xd4\x6e\x34\x4c\x37\xfe\xd6\xa4\x55\xe6\x56\x8b\x05\xa6\x16\x7b\xe2\x47\x5a\xa2\x13\x84\x47\xf2\x4b\x74\x86\x20\x47\xb7\xcc\x97\x98\x3e\xec\x2b\xcf\x04\xb1\xc4\xf4\xa1\xc7\x38\x93\xc6\x83\x85\xe9\x04\x0b\xd8\xb8\x51\x30\x75\x48\xc9\x99\x36\xc4\x8d\x83\xa9\x02\x8b\xba\x3b\x31\x30\x1d\xd8\x57\x85\xdb\x9a\x2c\xb1\xee\xe7\x9b\x50\x6e\x1c\x74\x82\xa0\x2f\x9d\xf4\xd2\x5d\x88\x2b\x54\x05\x7a\x44\x1e\x85\x70\xe2\x61\x9a\xc0\xf6\x93\xe2\x82\x1e\x3c\xf5\x61\xfa\xc0\xf1\x52\x5a\x76\x1e\xaf\x69\x85\xe9\x05\xc7\x6c\xfc\x4d\xc4\x54\x83\x23\xfe\xf5\xd2\x76\xf9\xc1\xed\xdb\xad\x30\x15\xf1\xba\x43\x2b\x4c\x41\xf2\x32\xa3\x65\x37\x22\x18\x7c\x0e\x61\x88\x23\xed\x43\xdd\x49\x92\xd2\x7e\x26\x8e\xd9\x06\xb1\x1b\x17\x5d\x27\xe4\x69\x77\x69\xdc\x66\x63\x8d\xe9\xcc\x99\xd4\x71\x3f\x42\x3d\x3d\xb8\x46\xfb\x9e\xef\xc3\x3b\x71\xb0\x5e\xef\x7c\xc3\x7a\x8d\x75\x37\xcd\x72\x0f\x06\xba\x56\x38\x11\x9f\x08\xb0\x6e\x66\x7b\x38\x6e\x14\xac\x83\xbd\x6e\xef\x1a\xeb\xd8\xb6\xa3\x75\xbc\x27\xe9\x97\xaf\xa4\x71\xdb\x90\x35\xd6\xaf\x07\xd2\x76\xe3\xa8\x1b\xac\x77\xc7\xb1\x30\x7b\xc0\xa2\x11\x4e\x0c\x4c\x1b\x6a\x72\x69\xdd\x02\xd9\x60\xca\xd0\x76\x95\x7b\x2a\xdd\x60\xca\x70\xa8\x1a\x7f\x5b\x30\x7d\x60\xc2\x1b\xc5\xc4\xd7\x90\xb4\x1e\xc7\xc4\xb4\x83\xfe\x95\xa6\x6e\xb5\xdd\xa0\xab\x88\x13\x7d\x6e\xaa\x11\x23\xbc\xc1\xb4\x43\x62\xfa\x8d\xcd\x03\xa6\x1d\x75\x71\x69\xd9\x9a\xc7\x8d\x86\x06\x0a\xf2\x72\x14\x0f\x53\x12\xbe\x5a\x1c\x41\xc4\x54\xa5\xfa\x32\x82\x84\x69\xcb\xdf\x2e\xb4\xed\x72\xb1\xa8\x73\xa3\x62\x3a\x93\x97\x87\x6a\x04\x0d\x55\x98\xb4\xa1\xb4\x6c\x4f\x95\xa7\x1b\x30\x75\x11\x72\x19\x59\x40\x3c\x60\x6a\x53\x7d\x19\x45\xc3\x1d\xcc\x72\x0c\x6f\x8b\x29\x0c\x69\x9a\xea\xab\x5f\x47\xb7\xa8\x83\xc1\xf0\xfc\x1a\xba\x45\x67\x19\x86\xe8\xf1\xb9\xb7\xa8\x77\xc1\xb0\xbc\x2e\xfe\x16\x53\x19\x36\x77\x78\x97\x49\x5b\x4c\x5d\x1a\xca\x4e\xa6\x1d\x2e\x85\x3b\x74\xb0\xc5\x14\x46\x20\xb2\xd3\x43\x6e\x4c\xd4\xc2\xbc\xa4\x05\x39\x93\x51\xfd\x9e\xa1\x91\xbe\x63\xee\xee\xc0\x19\x1a\xe8\x2b\x28\x71\xae\xb3\x66\x68\x98\xef\x90\xbb\xa7\xe1\x59\x82\xce\xf5\xaf\x94\x6d\x3d\xb8\xb1\x30\xe1\xf7\x58\x69\x51\xb9\x67\x9f\x19\x1a\x20\xfc\x4a\x9a\x32\x2f\x8f\x23\xc2\xc3\x44\x5f\x17\xa4\xf4\x54\x86\x1a\x77\x52\xd0\x32\x73\xc7\x30\x67\x68\x9c\xb0\x21\x65\x56\x39\x63\x8b\x33\x34\x4a\x98\x56\xe7\x33\x75\x3b\x59\x33\x34\x54\x78\x26\xc7\x92\x7a\x70\xd0\xe0\xb7\x98\x75\xdc\x43\x73\x86\x46\x0c\x25\x9e\x6f\x70\xce\xd0\xb8\x61\x43\xbb\xaf\xd4\xc7\x26\xee\x0d\x56\x75\xdd\xf7\x73\xea\x09\x3a\xcf\xd0\xe0\xe1\xa1\x2a\xd8\x36\xa4\x57\xb7\xd0\x28\xa2\xc0\xf4\xea\x32\x1a\x4a\x14\xf6\x40\x9e\xf3\x73\x23\xe3\xb1\x24\x86\x7c\xaa\x9a\xfc\xe7\xaa\xec\x3c\xe8\x78\x88\x31\x73\x3a\x39\x33\x34\xc2\xb8\xbf\x14\xc5\xa9\x6a\xdc\x4d\x44\xa3\x8c\x7b\xea\x36\x75\x33\x34\xca\x98\xf6\xd2\x38\xe4\x29\xe9\xdc\xdd\x80\x06\x1b\xbb\xd3\xe5\xbc\x6f\x7d\x1a\x8a\x46\x1a\x05\x9a\x57\x41\xd1\x60\xe3\x89\x94\x99\x7f\x8e\x9b\xa1\x01\x47\x86\xe7\x9b\x53\x67\x68\xd0\x91\xa1\xf9\x1a\x87\x29\x09\x43\xf2\x36\x0d\x8d\x39\x72\x5f\x21\x64\x1a\x9f\xa1\xe1\x47\x0d\xdf\xdb\x54\x34\x0e\xa9\xa1\x7b\x9a\x8c\x86\x24\x35\x64\x7f\xd3\x31\x2d\x3a\x16\xd5\xde\xad\x78\x68\x68\xf2\x6b\x43\x4b\xf7\xae\xd8\x0c\x0d\x4b\x76\xa4\xfd\xe2\x8c\xc6\xcd\xd0\x80\xe4\x21\x2f\x3c\x71\x97\x19\x1a\x8d\xdc\x37\x39\x3d\xa4\xc4\x63\xd1\xd0\x80\x64\xef\xda\x70\xef\xd6\x89\x87\xc6\x24\x33\xd2\x9e\xf6\x95\x67\xfd\x34\x43\x23\x93\x35\xa9\x69\x93\x16\xb9\xbb\xa7\xd1\xf0\x24\xdb\x59\xf4\xef\xfa\xcd\xd0\x28\x65\x91\x97\xce\xf5\xff\x0c\x8f\x50\x9e\x2a\x8f\x13\x80\x46\x28\xeb\x4b\x7b\xaa\xdd\xfb\x5e\x33\x34\x44\x79\x69\x3d\xa2\xc3\x3a\xf8\xb8\xf7\x08\x0d\xeb\xda\xb6\xf2\x4c\x8c\x68\x94\xb1\xc7\x88\xf7\xaf\x31\x29\xea\x13\xd9\x7b\x66\x64\x34\xd6\x68\x62\xfb\xdc\xed\x19\x1a\x75\x94\x14\xf8\x69\x1c\x27\x2a\x1a\x73\x80\xa8\xfe\x9a\xd1\xf5\x81\xe4\xbd\xeb\x9a\x7c\x7f\xe9\xdc\x7b\x16\x33\x34\x02\x69\xe3\xfb\x79\x40\x35\xa2\x64\xe1\x2a\xea\xd6\x0b\x34\x22\x49\x5f\x6a\x52\x7a\x70\xf0\x9d\x4d\x7e\xee\xca\x6f\x35\xd1\x50\xa4\x42\xf5\x58\x6b\x34\x1c\x59\x54\x47\xcf\xe6\xf0\x6c\x8d\xee\x75\x16\x9e\x0d\xd5\x19\x1a\xbd\xec\xab\xf1\x6c\x27\xcf\xd0\xf0\x65\x49\xbf\xc6\x5f\xf3\x32\xab\xbe\xba\xf1\x70\xcf\x35\xad\x3c\x26\x10\x0f\x63\x12\x77\x80\x71\x86\x46\x31\xbd\xee\x26\x1a\xc4\xec\xeb\xf0\xb0\x85\x6e\x67\xb0\xa3\x6e\x6e\x1c\x4c\x17\xe8\x8b\x17\x07\x8d\x5b\xb6\xd4\xa3\xac\x68\xcc\xf2\x50\x54\x75\xfd\x1a\x67\xee\x03\x47\x74\x86\x86\x2e\x05\xa2\x5f\x18\x68\x04\x53\x60\xfa\x0f\x41\xcc\xf0\x50\xe6\x50\xa9\x1b\x11\x0d\x67\x72\x44\x6f\x67\xa3\xd1\xcc\xb4\xa1\x59\xde\xf5\xeb\x20\x4f\x2b\x31\x2d\xe1\x57\x47\x3c\x96\x16\x8f\x67\x5e\xba\x82\x36\xee\x79\x18\x0d\x65\xf2\xc3\xb8\x4e\x1c\x34\x86\x99\x56\xe7\xba\xa1\x6d\xeb\xe9\x3c\x34\x88\x49\x49\xe3\x9f\xc4\xd1\x10\x26\x43\xf1\x1a\x6d\x34\x80\xd9\x55\x5f\x7d\xed\x42\xe7\x9a\x8e\x74\xee\xe9\x05\x0d\x5b\xb6\x99\x7f\xd7\x68\x86\x46\x2d\x4f\xa3\x58\xa8\xed\xb8\xec\xd9\xe1\x6f\x0f\x8b\xe8\x2e\x08\x3b\x91\xdb\x76\xb4\xf1\x55\x88\xfb\x29\x17\xb6\x74\x29\xf6\x6e\xa5\x42\x63\x96\x1c\x71\x15\xcf\xdc\x68\xb8\x9f\xd2\xa3\xad\x7d\x68\xb8\x73\xd2\xa3\x6d\x7c\x68\xe8\x22\x45\xde\xee\x8d\x7d\xbb\xe5\x33\x34\x6a\xd9\xd0\x63\xde\x76\xfc\x0a\xc0\x08\x3a\xba\x73\x5e\x54\x97\x6c\xf4\x7c\xcd\x0c\x0d\x43\x72\x5c\xff\x29\x9b\xd9\x16\x53\x84\xae\xa1\x34\x4e\xab\x32\xf7\x59\x96\x2d\x7e\x7c\x8a\xd2\x38\xa3\x69\x9e\x5d\x2a\xe7\x91\x4d\x3a\x4f\x50\x63\xe1\xe4\x72\x8e\x06\x4a\x7b\xfb\xec\x3d\x4a\x35\x47\xa3\xa5\xbd\x75\x1e\x41\x43\x97\x21\xf4\x99\x16\x1e\x8f\x69\x8e\x86\x4d\x7b\xd5\x71\x63\xa0\x2b\x11\xd2\xba\x63\x29\x73\x34\x5c\x4a\x0a\xea\x9e\xc2\xe7\x68\xf8\x92\xfe\xed\xc2\x6e\x82\x3b\xbb\x77\x8e\x46\x30\xbf\xe4\xa5\xf3\x1c\xcb\x1c\x0d\x5f\xfe\xed\xe2\x59\x97\xe2\x47\x77\x6b\xe2\x76\x68\xe7\x68\xdc\x72\x9f\xb7\x27\xf7\x7e\xe5\x1c\x8d\x58\x7e\x29\x7d\x91\x92\x39\x1a\xb0\xdc\x93\xfd\x6b\x7c\xa8\x9a\xf3\xa5\x70\x9e\x66\x99\xa3\xf1\xca\xce\x1d\xf7\x9d\xaf\x0f\xd8\x61\xeb\x7d\x41\xd2\x2f\xde\xe5\xf9\x1c\x0d\x53\xee\xdd\x73\xed\x1c\x0d\x4d\x92\xba\x76\x8e\x85\xc3\xc3\x01\x3b\xbf\x4c\x1b\x4f\x90\x62\x8e\x06\x24\x4f\xd5\xa5\xf1\x1d\x7b\x9e\x2f\x66\xd8\x11\xf2\x82\x9c\xdd\xfd\x8a\x46\x24\xb3\x4b\x5d\x78\xe3\x91\x73\x34\x1e\x59\xe7\xc7\xe3\x6b\xbc\x27\xee\x58\xc3\x1c\x0d\x48\xb6\x69\xde\xb6\x55\xe3\x36\x75\x68\x34\x72\x9f\x77\x69\xe5\x5e\x49\xcd\xd1\x50\xe4\xbe\x73\x1e\x55\xc2\x11\x5e\xf6\x6e\xfd\x46\x11\x5e\x9d\x43\x35\x49\x08\xd6\xfa\xbf\x3a\xad\x9b\x03\xa1\xb9\xec\x9d\xca\x36\x4f\xf6\x19\x8e\x72\x1d\x02\xbb\xf1\xe0\x6c\x38\x1a\x42\xcd\x53\x1a\x17\x55\x51\xb8\x6d\x35\x1a\x39\x55\x68\x71\xd7\x5b\x6d\xf7\xc0\x43\x03\xa7\x34\xbb\xa4\xfc\x6a\xa0\x13\x0d\xdd\x6f\x67\xa9\x31\x02\xb6\x12\xe6\x68\xc8\x54\xa0\x8f\x6d\x63\xcc\xd1\xe0\xe9\x99\x96\x97\xf8\x44\xce\xfb\x4b\x73\xf4\xcc\x1d\x68\x10\xf5\x5c\x65\xa4\x18\x59\xa2\xcf\xd1\x58\x6a\xe5\xbc\x5f\x41\xe7\x68\x20\xf5\xd8\x10\xcf\xe0\x42\x83\xa8\xed\xa5\x64\xe6\xc9\xed\x33\xcf\xf1\x83\x9d\x32\xf7\x89\x1b\x0d\x3d\xde\xd9\xa3\xf1\xbb\x6f\x4e\x3c\xf4\x1c\x78\x8f\x07\x6e\x12\x3a\x91\x51\xcd\xd9\xff\x95\xa6\x9d\x38\xa5\xe7\x39\xe0\x33\x47\xa3\xaa\x1a\xb6\xc8\x56\xe1\x24\x80\x29\x8f\x46\x20\x40\x7d\xd1\x98\xab\x46\xc4\xb7\x59\x31\x47\xcf\x88\x6a\xe8\xa3\x63\x00\x0d\xe2\x6a\x24\xbc\xdb\x2d\x73\xfc\x00\x69\x93\x93\xf2\x58\xd0\x11\x5c\xfc\x0c\xa9\xc4\xf5\xb6\x1c\x0d\xed\x2a\xd4\x91\xae\x43\xa3\xba\x0a\xd9\xa7\x35\x68\x50\x37\xad\xca\xb6\xf2\x98\x63\x3c\x94\x7b\xa9\x69\x23\x2e\x28\x3b\x11\xd1\xd9\xf8\xb2\x1f\x43\x43\x4d\x53\x6f\xd6\xfc\x22\x45\xcf\x19\xf6\x68\x23\xbd\x88\x69\x10\xc3\xf3\x85\x6d\xe7\x68\xd8\x96\xa1\x79\x16\x20\x43\xc8\xf6\xf7\xac\xf0\x57\x4a\x6e\x21\xea\xc0\xae\xea\xff\xba\x35\xea\x09\xab\x44\x82\x97\xa4\xd6\x32\x4e\x74\xa4\x8e\x4f\xf9\xf1\x54\xb0\xe5\xba\xb8\x5c\xdc\x1c\xf7\xe4\x43\x32\x89\xc4\xff\x93\x57\x41\x55\x66\x2a\xed\x26\xe7\xfb\x7f\xa5\xc5\x33\xed\xed\x42\xf4\x6f\xf4\x42\xdf\x4f\x22\xf5\x61\x12\xfd\x4b\x93\x93\x62\x62\x24\xc9\x82\xec\x2c\x39\x3b\xfa\x55\xce\xe9\x72\xfe\xb0\xda\xcc\x96\x0b\x90\x3c\xe6\xbb\xc5\x62\xf1\x88\xe6\x6e\xfa\xee\x70\x38\x48\x0e\xf5\xa4\x35\x68\xaa\x1a\x8d\x79\x90\xa4\x06\x70\x05\xbe\x6a\x8c\xe9\x09\x6c\x88\xd0\x28\xc9\xde\x86\xec\x37\x8f\x32\x45\x0d\xcc\x63\x00\xf3\x4f\xed\x58\xfe\x16\x3d\xa9\x94\x24\x31\x5f\xac\xe6\x9b\x14\x25\x01\x52\x21\x40\x3a\x0c\x5d\xe5\xa4\xea\x4e\x79\x29\xb2\x4d\x3d\xc2\xef\xab\xfa\x85\x25\xc7\x88\x86\xeb\xb1\xe9\xa5\x8d\x1b\x76\x92\xa4\xaf\x1b\x40\xc7\xd5\xe1\xd0\xd2\x6e\x17\xc5\x73\x95\xef\x08\xc9\x88\xa4\x72\xb4\x88\x8c\x01\x66\x32\xa7\x73\x9e\x65\xc3\x2d\xda\x94\x34\xd5\xa5\xa5\x05\x4f\xdb\xf2\x34\xcd\x3b\x7a\x7e\x22\x4f\x2c\x35\x01\x5e\xc8\x8b\xf2\xf3\x31\x6e\x68\x5b\x57\x65\x9b\x3f\xd3\x09\xbb\xe0\x7e\xba\x9c\xf7\x25\xc9\x8b\x48\xe2\xab\x2f\x4f\x92\x19\x3d\x77\x19\x4f\x45\xa2\xe5\x33\x78\xc4\x53\xbf\xf0\xfa\x7a\xd5\x12\x29\x29\xc4\x88\x6a\x48\x96\x5f\xda\x5d\xb4\x56\x12\x61\x90\x03\x2b\xbf\xf8\xae\x3d\x8f\xd4\xad\xa5\xbe\x19\x1f\x0d\xb8\xfa\xe3\xd9\x55\xbe\xcb\xb2\xec\xd1\x6e\xc6\xd2\xb0\x00\x0d\x29\xe5\x9d\x6e\x52\x14\xd1\x74\xde\x46\x94\xb4\x34\xce\xcb\xb8\xba\xb0\x41\x10\x57\x21\x50\x23\x20\x50\x74\xfc\x14\x03\x26\xe3\x95\x4a\x33\x26\xb2\x6c\x71\x8d\x63\xf3\x68\x34\x17\xc6\x4b\x7c\x93\x19\xc0\xe4\x67\x95\x27\x06\x34\x5a\xde\x4c\x97\x22\xa1\x54\x69\x65\xdb\xc4\x55\x59\x70\x8b\x36\x5c\x6b\x27\xfb\xb6\x2a\x2e\x1d\xbb\xd6\x2e\xbb\x8d\x93\x57\x1d\x52\x1b\xf9\x8a\x60\xb2\x9b\x58\x94\x9a\xc9\xc5\x98\x25\x2b\xf2\x7a\x17\x35\x34\xed\xa0\x71\xc5\x32\xdc\x48\xde\xf8\x48\x25\xfd\x12\x50\x66\xa3\x43\x8a\x06\x53\x30\x34\xa3\xed\x48\x97\xa7\xa0\x11\x52\xd7\x4c\xdd\xd3\x32\x77\x59\x89\xb8\x06\xb6\xc1\x38\xf9\xb1\xa9\x0a\x95\x56\x8b\x5b\x30\x34\x23\xd6\xf4\x34\x9b\x44\xd3\xd3\xbc\xff\xcf\xa2\xff\xcf\xb2\xff\xcf\xaa\xff\xcf\x7a\x12\xf5\x85\x32\x8d\x48\x5f\xd2\x17\x9c\xd6\xe3\x26\x5a\x26\x01\x58\x61\x49\x00\xa6\x33\x67\xbe\xb1\xe9\x69\x16\x4d\xd9\xe1\xd4\x9e\x81\x59\xa4\x7e\xce\xc1\xe7\xf9\xf0\x79\x01\x3e\x2f\x86\xcf\x4b\xf9\xb9\xb7\x46\xa7\xe5\x50\xb0\x02\xf0\xab\xe1\xf3\x1a\x7c\x5e\xcb\xcf\x80\x15\xc5\x09\xcb\x93\x32\x7c\x56\x9c\x00\x46\x06\x3e\x06\x36\xa2\x81\x87\x81\x85\x9e\x96\xe2\x01\xb0\x20\x39\x18\xa4\x3c\x9a\x52\x41\x5a\x99\xcd\x66\x83\x77\xeb\xd0\x8f\xa1\xe3\x75\x36\xa4\xd2\xbb\x4b\xa7\x0c\x34\xfa\x76\x2b\x22\xa1\xd2\x34\x5d\xa4\xf5\xea\x77\x8a\x3b\x5d\x63\x75\x2d\x85\x2d\x9d\x05\xb4\x74\x09\x78\xbf\x55\x6f\xa0\xf6\x61\x1d\x1f\x05\x76\xbb\xca\xcd\x08\xfb\x54\x64\x95\x04\x00\x0b\x30\xe5\xb1\x3e\x9e\x5b\x10\xb0\x85\x0b\xa5\x05\x30\x0d\xe5\x12\xca\x80\xb5\xc1\xf4\x49\x1f\x00\xc4\x8a\xb5\xc1\x84\x80\x34\xd6\xba\x9d\x10\x10\x83\xbb\x52\xeb\x9e\x4a\x94\x68\xdd\x50\xc8\xdc\x49\x8e\x49\x04\xd2\x5c\x83\x4f\x72\xa0\x2c\x50\xb3\xb3\x14\xe4\x45\x8e\xae\x0f\xd1\x39\x2f\xf9\xac\x1f\xed\x36\xeb\x87\xfa\xe5\x23\xab\x73\xa8\x5d\x93\xd0\xac\x67\x4f\x25\xdb\x91\xbd\x86\xa7\xe1\x1c\xba\xec\x4c\x9a\x2f\x93\x48\xe5\xf6\x1c\xb2\xb1\xcd\x79\xfe\x35\xcc\x55\x48\x0f\x0f\x74\x21\x09\x30\x2f\xb3\x5f\xc5\x31\x7c\xf6\x97\x70\xdf\xfa\x8f\x1a\x14\xcf\xd5\x6b\x82\xb1\xaf\x1a\x1c\xbf\x3d\x69\x01\xf2\xcf\x1a\xa4\xb8\xf4\x68\x81\x8a\xef\x1a\x6c\x59\x7d\x6d\x08\xef\xd6\xaf\xa7\xbc\xa3\x2c\x55\x1b\x4b\x0f\xd3\x7f\xd7\x9b\x53\x7d\xa5\x4d\x4a\x5a\x3a\x10\x06\xd9\x22\x55\xa9\x86\x73\xa9\x6b\x0f\x8e\x2a\xd5\x1b\x4a\x6a\x76\x19\xf6\x67\x1c\x69\x28\xd6\xb0\xce\x17\x99\xed\x0c\x31\xab\x0c\xa2\x6e\x72\x95\x83\x57\x5f\x5a\x48\xcf\x5f\x83\xc3\x56\x11\x0f\xeb\x64\x9b\x68\x44\xdb\x4b\x9a\xd2\xb6\xd5\x89\xa6\x9b\xf5\x22\xd3\x89\x0a\x38\x8c\xe8\x7e\xb5\x9c\xa7\x1a\xd1\xbc\x3c\x54\x3a\xc5\xd9\x26\x79\x38\xe8\x14\x7b\x20\x8c\xdc\x72\x35\x5f\x6f\x35\x72\xe2\x0a\x83\x06\xf6\x40\xd6\xd9\x62\xaf\x53\x14\x70\x08\xd1\xf5\x7a\x35\x33\x78\xcc\x48\x79\x34\xa0\xc8\x76\xb9\x5c\xce\x75\x9a\x1c\x0c\x21\xf9\xb0\x5c\xac\x16\x72\x6c\x4f\xf7\x47\xb4\x7b\xa4\xff\x6d\x0f\x37\xa3\xe3\x06\x7c\x50\x15\x82\xa6\xf7\xe0\xfe\xa8\xf5\x1f\x02\x9f\x1d\x0e\x49\xf6\x00\xab\xb1\x3b\x12\x41\x4b\x67\x74\xbe\x5f\x80\x6a\x54\x8f\x62\x75\x6c\x69\x76\xd0\x9a\x62\x74\x2d\x82\x43\x0e\xd9\x76\x70\xb7\xf7\x47\xad\x8f\xc7\xac\x13\x01\x08\xfe\x6a\x0e\x1b\x9a\xee\x57\xa0\x1a\xd0\xeb\x18\xf8\x3c\xa3\x19\x85\xb5\x58\xdd\x8f\x60\xd1\xe5\x7e\xbb\x57\x1a\xcb\x92\xd8\xf1\xf3\x3d\xd0\xf6\xaa\xc9\x64\x0b\xbd\x81\x1d\xcb\x1d\x1c\x25\xc6\x3a\x45\xcb\x11\x6d\xad\x4e\xaa\x62\x12\x5d\x0a\xcb\xcf\x48\xfc\x4e\x46\x55\x44\x3d\x62\x55\x44\x17\x8e\x2f\xc8\xe8\x94\x24\xa2\x52\x31\x96\x4f\xe3\x52\xb2\xa4\x5b\x5a\x02\x4e\x1e\xe3\x8b\xc4\x8c\xd7\x82\xbc\x5c\x2a\x12\xc1\x91\xf9\xa2\xd7\x85\x2a\xea\xe5\x5f\xe2\x95\x5c\xe4\x8e\xd2\x7b\x2a\x72\xff\xda\x5a\xd6\xd5\x88\x25\x81\xb6\x32\x13\xf5\xad\x94\x74\xb2\x30\x79\xce\x07\x79\x66\xd9\x24\xca\x90\xfc\xb9\xc3\x9a\x5c\x02\x76\xb6\x4b\x0d\xf2\x79\x6b\x1e\x87\x10\x4c\xa0\xc7\x90\x15\x20\xf4\x2f\x99\x79\x77\x28\x2a\xd2\xf1\x79\x5a\x66\x5c\x64\x2b\xd5\xb5\x50\x31\x74\xfd\xf9\x2e\x2d\x28\x69\x00\x96\x35\x97\x0f\x5f\x07\x7c\x5a\x14\x79\xdd\xe6\x2d\xaf\x07\x9b\x7e\x79\xea\x41\x83\x51\xe1\xe6\x68\x6d\x9e\x3d\x24\x9a\xa7\xc3\x52\x73\x66\xa4\x23\x71\xd5\xe4\xc7\xbc\x24\x45\xcc\x13\x75\x4e\x22\x33\xaf\xba\x5c\x61\x9e\x68\x51\x3b\xc6\x10\x8f\x7c\x69\x53\x6a\x5e\xe6\x2c\xf1\x5b\x7b\x36\xfd\xa8\x2d\x8f\xc4\x8c\x4d\xf6\x43\xe6\x4e\xdd\xc7\xea\xc7\x9c\xb1\xbc\xe1\xae\x26\xe6\x46\x6e\xa6\x2b\x6d\xe0\x2b\xbd\xb4\x87\x3d\xa8\xaf\x2a\x76\x05\x69\xbb\x38\x3d\xe5\x45\x36\x89\x40\x49\xed\x2a\xb8\x40\x14\x91\xd0\xd7\x31\xe6\x01\x96\xf4\x37\xc1\x27\xf9\x7a\x00\xf8\x34\x78\xa3\x76\x78\x4d\x4f\x13\x1f\x18\xcf\x1d\xba\xc9\xe2\x45\x84\xc8\x11\x96\xb0\x12\x88\x22\x1a\xad\xc2\xfc\xdf\xff\x65\x9e\xcc\x96\xd1\x5f\x92\xe4\x5f\x92\xef\xd5\x14\xa1\x70\xe3\x86\x3e\xd3\xa6\xd5\xe8\x4d\xeb\x4b\x51\x00\x87\xd7\xb0\x31\x33\xd4\xc8\x24\x8f\x98\x6b\x0c\x83\x6f\xca\x42\x81\x4e\xb7\x74\x22\x71\xb3\x68\x8a\x06\x03\xd1\x65\xc4\xf2\x9f\xda\x40\x2e\x09\xc3\x76\xeb\x75\x69\x19\x6c\x21\x98\xb3\x4f\x20\x90\xb7\x7b\x3c\x7d\x22\x99\x10\xdb\x26\x9e\xf6\x72\x08\x6f\x73\x05\x11\x6f\x6b\x15\x19\x6f\x63\xbd\x94\x00\xa1\xc8\xd0\xc3\x5e\x03\x23\xa6\x8d\xb2\xcd\x24\xcb\x1a\xe9\xd5\x79\x17\xa3\x66\x2e\x4c\xff\x54\x14\xf6\x16\xc0\x9f\x68\x59\x54\x93\xe8\x4f\x55\x49\xd2\x6a\x12\xfd\x81\xed\x3a\x92\x76\x12\xbd\xff\x43\x75\x69\x72\xda\x44\xff\x46\xbf\xbe\x07\x0f\x05\x00\xea\xba\x29\x9c\xd7\x2f\x32\xa4\x6c\xdb\x57\xe5\x6b\x6e\xe6\xab\x25\x75\xad\x4a\xb7\x87\xf9\x61\x89\x47\xaa\x45\xb5\x5f\xf6\xd9\x0d\xb5\xfa\x3c\xf3\x05\x52\xdf\x42\x8f\x8c\xc3\x24\xd6\x79\xd9\xd2\x2e\x4a\x58\x7c\x37\x4a\x8c\x1d\xb2\xe9\x7c\xf5\x51\xed\xc6\x85\x22\x80\x96\x59\xad\x33\x9f\xf2\x90\x1b\x07\xa6\x7f\xe1\xe2\x56\xbe\x94\x62\x7e\x93\x11\x12\xb1\x9b\x63\x5b\x72\xc5\xc1\x56\xce\x59\x66\x1c\xc5\xe4\x6c\x71\xed\x06\xde\xd7\xaa\xc9\x78\x02\xe8\x5d\x24\xf2\x40\x17\x85\x2a\xe8\x3d\x0a\xf9\xbd\xff\xe0\x52\x99\x55\xff\xcf\xb1\xed\x91\xa6\xa9\x57\x99\xfa\xe6\xdb\x7a\x6c\xca\xdc\xf9\x7e\xc5\xa3\x19\x86\xa8\x1b\xca\xf8\xc6\x79\x05\x4f\xcb\x20\x5c\x29\x8b\xdf\x13\x69\xd3\xa6\x2a\x0a\x95\x3b\xfa\x4c\x5e\x94\x48\x17\xcb\x44\xdf\x58\x88\x5f\x77\x11\x87\x57\xbb\x6c\xbd\xe7\x95\x1b\xef\x42\xf8\xa7\xad\x99\xd6\xc9\x12\x56\xdf\x1a\x10\xa0\x20\xfe\x3f\xe6\xb2\xea\x8c\x48\xdf\x74\xb3\xd2\x9d\x3f\x8c\xca\x76\x3b\x1f\xa1\xb2\xdd\x8c\x53\x99\xcd\x93\x64\x84\xcc\x6c\x66\xd0\x19\xe0\xe2\x43\x71\xc9\xb3\x5f\x5b\x86\xd3\xa6\xfa\x0a\x2d\xbf\x40\x8b\x0d\x6a\x62\xc9\x34\x1b\x16\x31\xd3\xb4\x2a\xe2\xe2\x18\xcf\x26\x91\xfa\x99\x80\xdf\xf0\xfb\x7c\xf8\x0d\x7e\x2e\x26\x5c\x2e\xec\x8f\xe5\xf0\x7d\x35\xfc\x5c\x0f\x3f\x37\xc3\xcf\x87\xe1\xe7\x56\xd1\x38\x67\x8a\x95\xfe\x67\x02\x7e\xc3\xef\xf3\xe1\x37\xf8\xb9\x80\x64\x96\xc3\xf7\xd5\xf0\x73\x3d\xfc\xdc\x0c\x3f\x1f\x86\x9f\x03\x2b\xed\x59\xb1\xd2\xff\x4c\xc0\x6f\xf8\x7d\x3e\xfc\x06\x3f\x17\x90\xcc\x72\xf8\xbe\x1a\x7e\xae\x87\x9f\x9b\xe1\xe7\xc3\xf0\x73\x60\xe5\xa5\x55\xac\xf4\x3f\x13\xf0\x1b\x7e\x9f\x0f\xbf\xc1\xcf\x05\x24\xb3\x1c\xbe\xaf\x86\x9f\xeb\xe1\xe7\x66\xf8\xf9\x30\xfc\xdc\x1a\xfb\x81\x30\x5b\x77\x3f\x54\xf0\xcd\xcc\x71\x4d\x87\x5a\xf8\x0f\xd2\x48\xb0\x16\x36\xb9\xe3\xdb\x15\x60\xf7\xdd\x04\x98\x41\x80\xed\x6c\xba\xe6\xff\xb7\xb1\x00\x13\x08\xf8\xb0\x98\x2e\xc4\xff\x99\x80\x5b\x08\x07\xf6\x57\x24\xf3\xb0\x78\xbd\x76\xd6\xb7\x81\x70\xab\x07\x67\x75\x6b\x0d\xce\x6a\xdf\x0a\x16\x2f\xdd\xcd\x5b\x42\xb8\x85\xbb\x75\x0b\x08\x37\xb7\x5a\xa7\x8b\xdb\xdd\x3a\x4d\xea\xee\xc6\x31\xc7\x5a\xf4\xa1\x54\x4c\xbb\x0f\x39\xd4\x0c\x42\x79\x3a\x92\x43\x27\x10\xda\xd3\x9b\x0c\x7a\x0b\x81\xed\x2e\x65\x30\x0f\x10\xc6\xd3\xaf\x0c\x78\x03\x81\x3d\x9d\xcb\x80\xd7\x1a\x30\xde\xfa\x15\x84\xf1\x74\x33\x03\x5e\x42\x60\x4f\x5f\x33\xe0\x05\x04\xb6\x3b\x9c\xc1\xe8\x1d\x34\xd2\x76\xad\x9f\x46\x9a\xae\xf5\x12\x9c\x3b\x15\x50\x7b\x92\xfa\x21\x2c\x14\xa6\x1e\x3d\xd0\x0c\x00\x79\xb5\xa3\x07\x4e\x00\xb0\x57\x39\xda\x93\x50\x0e\x0e\x8b\xe9\x46\x7b\x12\xba\xc1\x41\xbc\xaa\xd1\x9e\x84\x6a\x88\x00\x91\x4f\x3c\xed\x49\x68\x86\x80\xc5\xdb\xbd\x02\x20\x5e\xbd\x68\x4f\x42\x2f\x38\xac\x57\x2d\xda\x93\x50\x0b\x0e\x8b\x69\x45\x7b\x8a\xb5\x6e\x19\x69\x35\xec\x9d\x91\x46\xc3\xbe\x41\x54\x82\x9f\x5e\x93\x4a\xa1\x07\x1f\x6d\xdd\x90\xd0\x33\x1b\xda\xa3\x24\x12\x2b\xb1\xb1\x3c\xda\x22\xb0\xb6\x36\x92\xad\x36\x02\xf6\xc1\x86\xf5\xe8\x8f\x40\xda\xd8\x48\x1e\x45\x12\x48\x6b\x04\xc9\x25\xad\x95\x0d\xeb\x51\x2d\x81\xb4\xb4\x91\x3c\x3a\x26\x90\x16\x36\x92\xad\x6c\x02\x16\xeb\xf0\x51\x59\x21\xfd\x3e\x2a\x2a\xa4\xd7\x43\x43\xf9\xf7\xf1\x51\xdf\xec\xa4\x5a\x3b\x08\x32\x82\xaf\x2a\xd7\x97\x4a\x6c\xdc\xe8\x10\x33\x7d\x4d\xa6\x75\xbf\x0e\x99\x68\x90\xfa\xf8\xd0\x20\xb7\xc6\x62\xd1\x2c\x7f\xd0\xca\xf5\x71\xa0\x01\x6e\x34\x40\x5d\xf7\x35\xc0\xb5\x0e\x68\xb5\x72\xa5\x95\x2f\xdd\x8d\x5c\x6a\x80\x0b\x77\x1b\x17\x1a\xe0\xdc\x6a\xa3\x21\x78\x77\x1b\x75\xf9\xbb\x9b\x08\x3d\x28\xdd\x85\x42\xc0\x66\x1a\x98\xa7\x53\xa1\x0f\x85\x3b\x51\x36\xf8\x56\x83\xb6\xbb\x17\x78\x51\xb8\x1b\x65\x43\x6f\x34\x68\x4f\x47\x03\x3f\x4a\x73\xa4\x6c\xa0\x95\x06\xe4\xe9\x72\xe0\x49\xe1\xae\x94\x0d\xbd\xd0\xa0\xed\xce\x07\xbe\x14\xee\x4c\x21\x7d\xa0\x77\x81\xbf\x7e\xbd\xbf\xf8\xdc\x69\x40\x0d\xee\x94\xe6\x4f\x21\x50\x33\x08\xe5\x55\x95\xc1\xa1\x42\x3d\x2a\x1b\x7a\x0b\x81\x31\x45\x51\x2e\x15\xea\x53\xd9\xc0\x1b\x08\xec\x55\x13\xe5\x54\x41\xaf\xca\x86\x59\x41\x18\xaf\x92\x28\xb7\x0a\xf5\xab\x6c\xe0\x05\x04\xc6\x54\x44\x39\x56\xa8\x67\x85\x88\x5e\x93\xbc\xbf\x72\xad\x97\x10\xfd\xd0\x7d\x2b\xcc\xb9\x42\xc1\x67\x08\xb8\x47\x63\x74\xef\xca\xe7\x5e\x61\x68\x5b\x04\xcb\xd6\x21\xcd\xbf\xf2\x39\x58\x18\xd6\x06\xc1\xf2\x68\x95\xe6\x61\x21\x2e\x16\x06\xbc\x42\x80\x3d\x7a\xa6\xf9\x58\x3e\x27\x0b\xc3\x5a\x20\x58\xb6\xe6\x69\x5e\x96\xcf\xcd\x42\xfb\x12\xeb\xca\x31\xbe\xb0\xfe\x4f\xae\x0a\x1f\xdf\x23\x36\xf9\xe6\xe0\xa4\xd7\xd9\x62\x95\x7b\x9d\x2d\xc6\x6a\x90\xb3\xc5\x1a\x18\xe4\x6c\x0d\x6c\xe1\xce\x56\xdf\x82\x20\x67\xab\x6f\x75\x90\xb3\xd5\x4b\xca\xe7\x6c\xf5\x42\x0d\x72\xb6\xfa\x8e\x08\x72\xb6\xfa\xfe\xf3\x39\x5b\x7d\x57\x07\x39\x5b\xbd\x58\x43\x9c\xad\x73\x16\xe4\x6c\x29\xb0\x30\x67\x4b\x81\x87\x39\x5b\x12\xdc\xeb\x6c\x49\xa0\x30\x67\x4b\x42\x87\x39\x5b\x12\xda\xeb\x6c\x49\xa0\x30\x67\x4b\x42\x87\x39\x5b\x12\xda\xeb\x6c\x49\xa0\x30\x67\x4b\xf5\x41\x88\xb3\x25\x81\xfd\xce\x16\x83\x1a\x75\xb6\x14\x54\x90\xb3\xa5\xa0\x83\x9c\x2d\x09\xed\x73\xb6\x24\x4c\x90\xb3\x25\x81\x83\x9c\x2d\x09\xec\x73\xb6\x24\x4c\x90\xb3\x25\x81\x83\x9c\x2d\x09\xec\x73\xb6\x24\x4c\x90\xb3\xa5\x44\x1f\xe0\x6c\x49\x58\xaf\xb3\x75\xce\xae\x72\xb6\x00\xf8\x35\xce\x16\x40\xbb\xc6\xd9\x1a\xd0\x02\x9c\xad\x01\xf8\x1a\x67\x6b\xc0\xba\xc6\xd9\x1a\xb0\x02\x9c\xad\x01\xf8\x1a\x67\x6b\xc0\xba\xc6\xd9\x1a\xb0\x02\x9c\xad\x01\xf8\x1a\x67\x0b\xf4\x65\xb8\xb3\x35\x20\xdd\xe2\x6c\x19\xbb\xec\xf7\xd8\x94\x7e\xf3\xae\xb4\xd7\xdb\x62\x95\x7b\xbd\x2d\xc6\x6a\x90\xb7\xc5\x1a\x18\xe4\x6d\x0d\x6c\xe1\xde\x56\xdf\x82\x20\x6f\xab\x6f\x75\x90\xb7\xd5\x4b\xca\xe7\x6d\xf5\x42\x0d\xf2\xb6\xfa\x8e\x08\xf2\xb6\xfa\xfe\xf3\x79\x5b\x7d\x57\x07\x79\x5b\xbd\x58\x43\xbc\xad\xe2\x18\xe4\x6d\x29\xb0\x30\x6f\x4b\x81\x87\x79\x5b\x12\xdc\xeb\x6d\x49\xa0\x30\x6f\x4b\x42\x87\x79\x5b\x12\xda\xeb\x6d\x49\xa0\x30\x6f\x4b\x42\x87\x79\x5b\x12\xda\xeb\x6d\x49\xa0\x30\x6f\x4b\xf5\x41\x88\xb7\x25\x81\xfd\xde\x16\x83\x1a\xf5\xb6\x14\x54\x90\xb7\xa5\xa0\x83\xbc\x2d\x09\xed\xf3\xb6\x24\x4c\x90\xb7\x25\x81\x83\xbc\x2d\x09\xec\xf3\xb6\x24\x4c\x90\xb7\x25\x81\x83\xbc\x2d\x09\xec\xf3\xb6\x24\x4c\x90\xb7\xa5\x44\x1f\xe0\x6d\x49\x58\xaf\xb7\x55\x1c\xaf\xf2\xb6\x00\xf8\x35\xde\x16\x40\xbb\xc6\xdb\x1a\xd0\x02\xbc\xad\x01\xf8\x1a\x6f\x6b\xc0\xba\xc6\xdb\x1a\xb0\x02\xbc\xad\x01\xf8\x1a\x6f\x6b\xc0\xba\xc6\xdb\x1a\xb0\x02\xbc\xad\x01\xf8\x1a\x6f\x0b\xf4\x65\xb8\xb7\x35\x20\x8d\x79\x5b\x9d\x3a\x01\xea\x3d\x4d\x2a\x4f\x64\x13\x96\xa0\x8e\xc1\xcb\xf3\x5a\xec\x6e\xd3\x83\x7e\x86\x4b\x9e\x2d\x17\x9f\xc1\x35\x0c\xf3\xee\x02\x38\x49\xd5\x9d\x18\x5d\xe7\xdd\x60\xc5\xa9\x91\xe1\x04\x49\x7a\xe2\xbe\x65\xc5\xc9\x3c\x75\xfb\x2a\x7b\x7d\xea\x9a\xa7\x2e\x9b\x44\xd6\xb7\xd3\xf0\xed\x50\x55\x9d\x09\xa7\xbe\x9d\x78\x9a\x18\xfe\xf5\x44\x49\x66\x42\xaa\x6f\x27\x28\x32\x25\x17\xcf\x41\x66\x33\xc9\x4d\x57\xd5\x9e\x4c\x23\x59\x96\x19\xed\x33\x6a\x36\xc9\x71\xc9\x20\x97\x9b\xe6\x1e\xa2\xa2\xf7\x3f\x49\xe2\xbb\x43\xde\xc8\x1b\x40\xb0\xd9\x7e\x38\x28\xb4\xb4\x2a\x7a\x95\xab\xc7\x49\xfa\x01\xad\x8e\xd0\x8b\x9d\x64\xc7\x61\x4f\xe2\x1a\x09\x14\x7c\x82\xe8\xd2\xa7\x4e\x65\xac\x82\xa0\x1e\x71\x46\x53\xdf\xd8\x03\x89\xa6\x38\x5c\x9c\x56\x65\x46\xcb\x96\x66\x98\xf2\xa2\xa5\x40\x2a\xb0\xdc\x56\x69\xb4\xd4\x81\x6d\xab\x39\x5a\x6a\x28\xfc\xca\x18\x80\x31\x17\x92\x96\xfb\xc8\xab\xd1\x0a\x01\x6d\x3d\x52\x08\xd9\x1f\x8a\x91\xb6\x23\x85\x38\x2e\xd2\x72\xa4\xf0\x74\x43\x8b\xae\xa7\x2c\xc6\xab\xb4\x7b\x73\x53\xbc\x6d\xd7\xe4\x35\x90\xc7\xae\xec\x4e\x71\x75\x88\xbb\xd7\x9a\x7e\xa8\xb2\xec\xa3\x53\xed\xb6\xfd\x3f\x9d\x18\xbb\xac\x3c\x90\xf2\x5f\x90\x66\x97\x25\xb4\xc9\x25\xad\x8a\x1f\xd3\x82\xb4\xed\xef\x7f\xe8\xe7\x26\x7e\xc7\x12\xcb\x1e\xa4\xae\x88\x48\xb5\x2a\x2e\x67\x76\x99\x54\x2c\xb2\xc1\xad\x12\x4e\xb9\xcb\x34\xc2\x93\x48\x7c\x3e\xdd\x56\x1f\xe5\x77\x43\xec\xda\x8c\x09\x62\xca\xf3\x23\x61\x73\x87\x2a\x3a\x21\xd3\x4a\x26\x4a\xa1\xb1\x1a\xf4\x75\xaa\xb2\x2e\xe9\xd3\x0c\x56\x9b\x59\xa4\xd9\xbf\x41\xc7\x5d\x24\xb1\xda\x84\x9a\x81\xda\xec\xb9\x0d\x6b\xdd\xa0\xbb\x2e\x92\x43\x6d\xd2\x96\x8e\xa8\x0d\xaa\x76\x82\xc4\x4e\x7c\x1d\x46\x8a\x17\x0c\x8e\x64\x4c\x8d\x9f\x0c\xa6\x0d\xa0\xa1\x55\x1c\xdc\x49\x6d\x68\xe3\xc8\xd5\xfe\x87\xfe\x9f\x43\xad\x44\x26\x05\x54\xaf\x54\x19\xae\x58\xa2\xd8\xa1\x59\xb2\xd4\xd2\x1f\xac\x46\xab\xcc\xa5\x5c\x2e\xaa\x68\x8d\x52\x83\x40\x8d\x88\x7e\x61\xad\x04\x0a\xe6\xa2\x0a\x6a\x74\xab\x98\x96\xb9\x02\xd7\x1d\x2d\x95\x85\x47\xc7\x0c\xb8\x71\x25\x33\x18\x47\xb4\x4c\x23\xe9\x55\xb3\xa0\x7c\x1b\x59\x42\xb7\xe9\xda\xa1\x67\x79\x79\xa8\x50\x25\xe3\x05\xb8\x86\xf5\x65\x0e\xf5\x62\x45\x96\xfe\x58\xb5\xe8\x05\x2e\xad\x42\x89\xd9\xb5\x48\x8d\x91\xb5\x20\xca\x64\xb5\x06\x68\x12\x4a\x4c\xd6\xe2\xd1\x21\x98\x99\x04\xd7\x8d\x21\x55\x89\x47\x81\x20\xd0\xb8\xf6\x40\x66\x11\xd5\x19\x88\x79\xf5\x66\x3c\x87\x4a\xba\xa4\x8b\xc3\xc2\xa1\x34\x22\x3d\x0a\xaa\x37\xaa\x0c\x57\x1d\x51\xec\xd0\x1e\x59\x6a\xe9\x09\x56\xa3\x55\xe6\x52\x23\x17\x55\xb4\x46\xa9\x31\xa0\x46\x44\x9f\xb0\x56\x02\x95\x72\x51\x05\x35\x7a\xe6\x3f\x3d\x59\x16\xa6\x33\x5a\x7e\x1a\x8f\x6e\x19\x70\xe3\xea\x65\x30\x8e\x68\x98\x46\xd2\xab\x64\x61\x49\x74\xc8\x61\x9e\xa6\x0e\x3d\xe3\x09\x72\x50\x35\x93\x45\xb8\x96\xf1\x52\x87\x92\x89\x42\x4b\x8f\x90\xda\xcc\x22\x97\x86\x39\x48\x62\xb5\x49\x0d\x1a\x6a\x43\xd4\x0b\x69\x1d\xd0\x2e\x07\xc9\xa1\x36\x8f\x6e\xc1\x24\x44\xb8\xce\xc0\xac\x44\x1e\xd5\xd2\xc1\xc6\x35\x4b\x67\x1a\x51\x2c\x48\xd0\xab\x57\x41\x69\x93\xf6\x69\x6a\xa8\x15\xc8\xe8\xcb\xb0\xe0\x6d\xc4\x69\x32\xfb\x9d\x76\xc9\xf9\x05\xbb\x78\xcc\xdf\x6e\x8d\x48\x99\x45\x1f\x86\x40\xd3\x66\xbd\x51\xdb\x91\x68\x45\x66\x8c\xca\xca\xa3\xb4\x32\x72\xe7\xc4\xaf\x5a\xf6\x9c\xf8\xdc\xaa\xb4\x38\x32\x29\x43\xff\xad\x67\xf0\x94\xb3\x18\x1b\xbf\x90\xbd\x27\x2c\xa3\xb2\x73\xc1\xc9\x63\x82\x26\x8f\x4f\x20\xf6\x80\x64\x6d\xf1\x22\xa1\x0b\x70\x1f\xd4\x89\x6f\xb7\x3a\x01\x91\x35\xb9\x0f\x6a\x8c\x1c\xb2\x98\xf6\x41\x9d\x7a\x6a\xbc\xcb\xdc\xf9\x86\x1c\x64\xf4\x28\x06\xcc\xac\x1b\x86\x06\x45\x09\xc3\x4e\xde\x16\xa2\xc1\x8e\x5b\xb0\x07\xb9\xbf\x09\xfb\xa6\xba\x87\x4e\x7a\x13\xb6\x56\x37\xec\x04\x3d\xd2\x7d\x65\x4f\x80\x8c\x47\xd7\x77\xc4\xf5\xc8\xa0\x1f\xde\x80\x7c\x4b\xcd\xa0\x17\xde\x80\x0c\x6b\xd6\xfa\x40\xec\x91\x5e\xdf\x09\x80\xa0\x98\x2c\x6f\x45\xf6\x1b\x0b\x5b\x8e\xb7\xd5\x8c\x21\x9f\x34\x51\x18\xc6\x95\xcd\x30\x87\x9c\x16\x59\x4b\x3b\x35\x33\x89\x39\x23\x71\x67\xfc\x4e\xb0\x04\xde\x05\x3d\xd2\x92\x4b\xde\x4e\xb2\x62\xcc\x43\x18\x59\x5f\x5a\xda\xf9\x0c\xd9\xa9\xb0\xb3\x95\xe8\x39\x6e\xf4\xac\xe8\x58\xea\xc1\x55\xff\x4f\xb2\x4f\xf6\xf4\xfa\xb4\xf6\x06\xeb\x2b\x24\x7b\xee\x90\x13\x8f\x3d\xd1\xf0\x63\xf7\x5a\xd3\x1f\x5a\x4a\x9a\xf4\xc4\x63\x82\xbf\xee\x8b\x18\xa0\x52\xf6\xee\xf4\xbe\x7a\xf9\x49\xbc\x16\xc1\xbf\x36\x24\xcb\x2b\xce\x89\x4a\xde\xc8\xf2\xf7\xc0\xbe\x51\x3b\x3d\x7f\xd9\x5a\xfd\xc0\xb3\x34\xd9\xd5\x1d\x72\x99\xc9\x4e\xd7\x07\x1b\xb2\xe9\xdd\x2a\x0c\xf4\x11\x4d\x65\xc0\xdf\xb8\xf8\xf1\x7c\x29\xba\xbc\x66\xa9\xf3\xc4\x97\x5e\x59\x38\x19\xe4\xa9\x03\x93\x33\xf9\x2c\x05\x26\x20\xa4\x8c\x8b\x89\x17\x08\x27\xf7\x57\x7f\x85\xa2\xba\x74\xf5\xa5\x73\xc8\x45\xdb\xf8\xdc\xd8\x59\x8a\xc2\x9f\x19\x59\xad\x54\xc8\xf3\x50\x35\xe7\x38\xad\xca\xae\xa9\x5c\x99\xef\x1c\x0f\x3b\x2c\x96\xc6\x1b\x01\xeb\xfa\x85\xa5\xa8\x7e\x13\x5f\xae\x5c\x48\x56\x96\xab\xfc\x4c\x8e\x14\x66\x81\x0a\x4f\x91\x34\x96\xff\xaa\xa7\xd1\xff\x7f\x23\x9f\x55\xb2\x71\x67\xc0\x1a\x41\xc1\xde\xa1\x10\x7c\xb1\x16\xc2\x37\x24\xa2\xe9\x6c\xd5\x4e\x22\x9b\xc1\xde\xac\x9b\x70\x8f\xf6\xcb\x15\x23\x74\x07\x7a\xfa\xc3\x15\xef\xde\x49\x7a\x6f\x26\xc6\x0a\x31\x05\x03\xaf\x38\x40\xc2\x2c\xaf\x31\x39\xd0\xad\x36\x96\x9c\x89\xbf\x02\x64\x3e\x61\xa9\xbc\x58\xca\xf5\x77\xef\x58\xf9\x2c\x99\x4f\xa2\xd9\x66\x35\x89\xe6\x8b\xc5\x24\x9a\xae\x6f\xe9\xca\x10\xb2\x68\xbb\x77\xcc\xa0\xd7\x05\x49\xe9\xa9\x2a\x32\x23\x03\xf3\x76\xcb\x5b\x5e\x93\x34\xef\x5e\x77\xd1\x0c\xa5\xd1\x2f\xc3\x98\x79\xf2\xd1\x71\xd4\x2e\xa4\x78\x13\xfa\x8f\x59\xce\x9e\xe0\xc8\x7e\x9a\x44\x7a\x41\x43\x49\x56\x95\xc5\xeb\x4f\x93\x48\xfa\x14\x03\xb0\x0e\xeb\x8e\x12\x88\x14\x91\xfe\xc6\x43\x1e\xc6\xaa\xe2\x6d\x12\xa9\x54\xcb\xaa\x8b\x49\x51\x54\x5f\xa9\xdc\x04\x95\xef\x24\xd9\x38\xfe\x29\x04\x9b\xc0\x49\x5d\x53\xd2\x90\x32\xd5\xd3\xfb\x22\x4b\x78\x89\xd1\xbb\x5b\x19\x7d\xce\x53\x1a\xd7\xf9\x0b\x2d\x62\xf6\xf8\xd1\x2e\xe1\x6b\x7a\x50\x5d\x46\x3a\xaa\x4f\xdb\x5d\x7e\x36\xbe\xf4\x30\xfd\xd7\xb8\xa8\x52\x52\xe8\x65\xe7\xaa\xec\x4e\x3f\xa9\x35\xa6\x66\x7c\x17\x4b\x99\x16\xec\xdd\x94\xab\x04\x3b\xe3\x10\xb7\x67\x93\x7a\xdf\xe5\x6e\x08\xce\x11\x33\x1d\x7e\x32\x1a\x93\x1e\x58\xce\x34\xa3\x68\x32\x22\xb0\xda\xb3\x2d\x13\xbc\xc8\xa8\x77\x00\x32\xa8\xf3\x3a\x55\x71\x84\x88\x2b\xc1\xc5\x55\x1c\xc7\xc4\xa5\x43\x38\xc4\x65\x93\x71\x8b\x4b\x87\x1d\x17\x57\x71\x74\x8a\xcb\x28\xc2\xc5\x55\x1c\xbd\xe2\x2a\x8e\x88\xb8\x96\x6b\x3d\xe9\x1c\x1b\x67\x8c\x7d\xe0\x72\x6a\x01\x29\x95\x84\x42\x78\x63\x93\x68\xca\x9c\x2f\x63\x03\x1d\xa6\xf7\xc2\x9e\xbf\xba\xea\xa5\x12\x59\x57\xc4\x96\x01\xaa\xc6\x61\x51\x00\x43\x76\x72\x81\xa2\xa7\x0a\x73\xbc\xfc\x92\x58\x0b\x02\xf1\xee\x8c\xe3\x01\x21\xc5\x09\xee\xb8\xab\x72\x99\x7f\x1c\x05\xeb\x75\x93\xb7\xc0\xf2\x60\x65\xdb\x10\x7c\xb0\x12\xc0\x1f\x87\x82\x42\x5d\xaa\xc5\x80\x9e\xb7\x6f\x8e\x48\xf5\x93\xd5\x97\x9f\x40\x97\x42\xaa\x31\xd2\xff\x82\x53\x93\xf1\x00\x6d\x70\xe5\x4e\xbf\xad\xc3\xf0\x07\xdf\x02\xba\x52\x70\xfc\x69\xa4\x45\x9f\xec\xf6\xb9\xf2\xb6\xcb\x63\xba\xe0\xbd\x3a\x6b\x16\xc4\xf5\xc7\x0f\x28\x54\x44\x9f\x7b\x14\xf6\x54\xe2\xe0\x8b\x26\x38\x2b\x5b\x34\x07\x5c\x9d\x38\x2f\x05\xdc\xf8\xa7\x6b\x53\x84\x80\x25\x4d\x7a\xe0\x3b\xe6\x20\x98\x1d\xd1\xf7\x38\x02\x66\xf5\x47\x00\x63\xaa\x66\xdd\x94\x58\x9f\x7d\x6c\x09\x18\x1f\x57\xc0\x34\x79\x98\x82\x4e\x4d\xcc\x4f\x1c\x59\xd6\xcc\x58\xb8\x69\xeb\x49\xf3\x20\xed\x06\x1f\x31\x9e\xea\xc0\xf4\xe2\x29\x16\x13\xad\x91\x92\x31\x41\x92\x8e\xcb\xaa\x34\x3c\x38\x35\x3f\x1a\xe7\xfd\xd0\x0c\xbe\x73\x74\x0d\xba\xc2\xf3\x23\x6b\x51\x07\x7f\xc5\x88\xa3\x80\x46\x2c\x80\x8f\xa2\x1c\x50\x94\x30\xcc\xf7\x35\xcc\x9c\x3d\x94\xdb\x61\xfd\x2d\xa4\x70\x2d\x2f\xc1\x82\xf1\x10\x06\xb2\xf2\x40\x89\xa5\xc5\x95\x02\x84\x83\xc3\xe4\x5d\x1b\x2c\xf3\xb7\xc9\x56\x57\x5f\xe1\x30\x41\x57\xe9\xd1\x7a\x72\x41\x7b\x92\x6b\x78\x3d\x0c\xa1\x2e\x8e\xe0\x23\xfd\xb7\xc6\xb5\xd8\x51\x3f\xe2\xbf\xf9\xb4\xb8\x1f\xdb\x86\x16\x1b\x84\x1d\x9d\x50\x1c\x3d\x9a\xf3\x1b\x0a\xe3\x5a\x96\x82\xe5\xe3\x21\x8c\x2b\xb3\x09\x15\xa6\xcc\x26\x16\xa6\xcc\x92\x77\x4d\x99\x1f\xee\x22\x62\xc9\xd1\x89\xb4\xf1\x81\xd2\xac\x5f\xd5\x3b\xbc\x33\x14\xd4\x16\xb8\x31\x0f\x2c\xe7\x53\xe0\x16\x6a\x2d\x75\xd4\x07\x9d\xd6\xc1\x79\x82\xd3\xca\xcf\x71\x5e\x66\xf4\x65\x17\xcd\xf1\xf5\x83\xbc\xfe\xb9\xd4\x1f\x42\x5d\x60\xd1\x4b\xf9\xd1\x7e\xfe\x8d\x49\x97\x3b\x85\x31\x7d\xa6\x65\xd7\xea\x07\x7a\xe5\x68\xf9\xe4\x69\x95\xbc\xb1\xba\xd6\x39\x19\x53\x46\x35\x9f\x04\xd0\x96\x96\x2e\xd4\x6a\xb3\xfe\x93\x2f\x74\x41\xdf\x1e\xfd\x3e\x78\xbb\x7a\xb1\x60\x48\xf9\x3a\x7a\xf1\x89\x16\x35\xf7\xdc\x4d\x44\xe6\xff\xa0\x1f\x81\x3b\x07\x4b\xed\xd5\x1d\x56\x28\xfd\x3c\x94\x1f\xe8\x74\x19\xf8\x9a\x93\x08\xbc\x32\xec\xd1\x39\x9d\x67\x4b\xef\x8d\x28\x28\x47\xfd\x6d\xc3\xd3\xa3\x8c\xba\x83\xb6\xfc\xc1\xbc\x37\x87\x69\xd7\xf5\x4b\xf4\xdd\x7a\xb3\x9f\xad\x1f\x6e\x8e\xc9\x02\x1a\x68\x83\x60\x10\x85\x64\x99\xb8\x4a\x66\xcb\xdd\x79\x7e\xf7\xd1\xd5\x5b\xa3\xf2\xd3\x07\xa0\x1b\x59\x3e\x1c\x67\x0e\x2f\xeb\xbb\x3e\xbc\x86\x62\x74\x78\xa9\x62\x6b\x78\xa9\x12\x38\xbc\xf4\x8f\xe6\xf0\x12\xa5\xf8\xf0\x32\x0b\xf1\xe1\x25\xa1\xec\xe1\xa5\x95\xb8\x87\x97\xf6\x5e\xa2\xce\xf3\xd8\xf0\xe2\xa8\x7f\xbf\xe1\x85\x32\xea\xdb\x13\x59\xcd\xee\x35\xbc\xd2\x84\xcc\xd6\xfb\xb7\x0d\x2f\x4e\x03\x6d\x90\x7f\x78\x0d\x72\x77\x9e\x40\x45\x86\x57\x60\x47\xe3\xc3\xcb\x46\xa6\x4d\x53\x35\xd6\xe0\x32\xbe\xea\x43\x4b\x16\xa2\x03\x4b\x14\x5a\xc3\x4a\x7c\x87\x83\x0a\x7e\x32\x87\x14\x2b\xc3\x07\x94\x5e\xa4\x0f\x27\x08\x01\x87\x92\x4e\x76\x64\x28\x69\xcf\x84\x42\x4e\xc7\x06\x12\x47\xfc\xfb\x0d\x24\x84\x4d\xf7\x30\xe2\xef\x9b\xde\x69\x18\xd1\x87\xe5\xc3\xe2\x8d\xc3\x88\xd1\x40\x9a\xe3\x1f\x44\x83\xcc\x9d\x47\x6d\x91\x41\x14\xd4\xc5\xf8\x10\xb2\x51\x15\x18\x57\xa5\xff\xed\x21\xc2\x2f\xa1\xae\x74\x07\x52\xc7\x97\x8f\xfc\x8f\xd3\x51\x31\xa0\x61\xb0\x39\x8e\x2a\xc0\xf0\xe9\xca\xb9\x07\x00\x8f\x1c\x6c\x16\xfd\xbf\xc0\xe4\xd6\x8c\x4f\x31\x9c\xcc\x7d\x0d\x77\x10\x1a\x8d\x37\x3b\x82\xcb\x72\x9f\xc9\xae\x09\x0e\x47\x4f\x5d\xc2\xc1\x97\x4f\xf3\xdc\x5a\x0d\x5c\x51\xe2\xb5\xa1\x24\x80\x0a\xe3\xa8\xec\x04\xdb\xb5\x8c\x41\xaa\xc6\x9a\xda\x03\x68\x0d\x27\xbe\xf5\x16\x86\xb0\xef\x4a\xed\x3c\x35\xcc\xa7\xe1\xa2\xf0\x84\x74\x14\x92\x84\x49\x43\xd7\xe6\x16\xfc\x58\xf4\xb5\xd2\x02\x33\x9c\xf6\x7d\xd8\x7f\x19\x57\xd5\x61\x09\x7d\x37\x76\xd4\xc4\x85\x30\x05\x5a\x6f\x45\x7f\x7d\x24\x1d\x3b\x66\x48\x0d\xd6\x06\x84\xda\x19\xc7\x77\x94\xd0\xc4\x15\x06\x1b\xee\x98\x86\x6e\xc3\x94\x11\x33\xf6\x44\xc1\x4b\xb2\x56\xa7\x61\x65\xd0\x75\xb0\x80\xa4\xbf\x81\x17\x60\x6f\x17\xc3\xb0\xff\xd8\x73\xc1\xc9\x4d\x8c\x83\x1d\x3f\xb8\x99\xba\x31\x42\x3b\x10\x05\xdf\x2c\x0e\x7e\x38\x2c\xc8\x80\x6b\xec\xdb\xe3\x0f\x93\x0f\x3e\x0a\xac\xb7\x50\xbd\x5d\x1c\xaa\x2e\xe0\x79\xaa\x5b\x5b\x65\x84\x0a\xc7\x1a\x39\x5b\x4e\x17\x8b\xd1\x17\xe7\x42\x6b\x6c\xcf\xe3\x35\x1a\x47\x04\xa4\xb5\x1d\xdb\xc7\xd5\xcf\x18\x3a\xb7\x71\xf5\x73\x87\xd8\xce\xae\xe7\x2c\x22\x1e\xd3\x43\xee\x68\xf8\x77\x88\xe3\x73\x1b\x77\xd5\x25\x3d\xc5\x24\xe5\xe6\xe5\x4c\xca\xbc\xbe\xf4\x26\xa6\x2a\x79\x98\xd2\x57\x6a\xec\x30\x43\xf7\xf5\xd2\xd2\x26\xe6\xa1\xe7\xe1\xfc\x23\x3b\x60\xe6\x28\x69\xf1\x02\xf4\xe3\x35\x27\x2c\xfd\xaf\x3e\x0e\x6f\x51\xf6\xfd\x2b\x6e\xa9\x4f\xc5\x39\x5b\xf0\x69\x07\x3f\x81\xdf\x3b\x0d\xa3\xd7\xba\xe1\x23\xc4\x01\x9e\xff\xaf\x7e\x3e\xd7\xe2\x11\xfe\x1e\x2e\x89\x19\xa7\xd3\x99\x4a\x65\x34\xad\xd8\x61\xaf\x52\x8f\x08\x0f\x82\xd0\x9a\x6d\x5e\x36\x33\x3a\x23\xfc\x94\xe4\xa2\x7e\x61\xcd\x36\x16\x25\x33\xcf\x73\xae\x1e\x14\xc8\x35\xd8\x89\xdf\x77\xe5\xd8\x11\x3d\x39\xc6\xb1\xe8\xf8\x23\xbe\xa9\xcd\x46\x6f\x5e\x74\xbd\xd6\x91\xa2\x3e\x91\x0f\xe2\x94\x60\xf4\x43\xb4\xd6\x0f\xd6\x86\x3c\x0c\xab\x1d\x33\x9c\xae\x57\xa0\x2d\x71\x46\x0f\xe4\x52\x74\x58\xef\x79\x4e\x23\xeb\x6b\x2d\x70\x09\x10\x90\xd4\x3a\x57\x7d\x04\x7a\x23\xbf\xed\x40\x92\x0e\xf8\x19\x03\xe5\x17\x17\xa3\x69\x55\xd3\xf2\x69\x9a\x35\x55\x9d\x55\x5f\xfb\xf9\xfb\x78\x2c\x28\x84\x54\x8f\x51\x8f\x35\x89\xae\xfb\x7f\xd8\x0a\x32\xeb\xff\x85\x36\x6c\xa7\x3e\x86\xb2\x86\xeb\x38\x56\x9f\xae\x6f\xd6\x67\x5c\xd0\xaa\x54\x33\x23\x56\xf1\xce\x8b\x8c\xf5\xc1\x50\x38\xdc\x22\x05\xc5\xda\xa9\x59\xf4\x3b\xca\x2f\x28\x46\x19\x1e\xca\x51\x8e\x41\xf1\x18\xba\x50\x21\xc7\x50\x95\x08\xa3\x00\x20\xb5\xcc\x18\xa4\x10\xe2\x08\xd8\x2e\x98\xe0\x2e\x90\x20\x6b\xa9\xe7\x9a\xf2\x15\x83\x39\x9a\xee\x49\x76\xa4\x9a\x99\xf0\x3e\xc0\x0d\x69\xd4\x4d\x7e\x26\xcd\x6b\x30\xf2\x86\xec\x37\x08\x67\x73\xba\xce\xc8\x12\x21\xac\x2b\x94\xfc\x08\x55\x57\x7c\x33\x2d\x8d\xfc\x8c\x81\x8e\x5b\x1a\x01\x69\x58\x1a\x77\xc3\xe6\x0f\xeb\x64\x9b\x60\x0d\x4b\x96\xd9\x26\xb8\x61\x21\x96\x46\x67\x6d\xdc\xd2\xc8\xfa\x0c\x4b\x63\x7e\xc6\x05\xed\xb0\x34\x66\xf1\xce\x8b\x8c\xf5\x81\xcb\xd2\x88\x62\xcb\xd2\x58\xdf\x51\x7e\x9d\x96\xc6\x2a\x47\x39\x76\x5a\x1a\xbb\x7c\xc4\xd2\x08\x84\x51\x80\x00\x4b\x63\xe8\xfc\x08\x58\x80\xa5\x31\x46\xc6\x18\xd8\x88\xa5\xb9\x7e\x48\x63\xf6\x06\x50\xf1\x66\xef\xeb\xc9\xc8\x0d\xc9\xb0\x91\xb9\x4a\xf7\x0f\xab\x14\xe1\x6f\x99\x12\xba\x4c\x11\xc2\xba\x66\xc9\x8f\x50\x87\x65\xd6\x20\xc3\xe4\xc8\xcf\x18\xe8\xb8\xc9\xd1\x73\x2e\x8d\x37\x6c\xb9\xdc\x66\xcb\x25\xb6\x85\xbb\x7d\x58\x2e\xb6\xa1\x0d\x0b\x31\x39\xce\x74\x50\x0e\x93\x23\xeb\x33\x4c\x8e\xf9\x19\x17\xb4\xc3\xe4\x98\xc5\x3b\x2f\x32\xd6\x07\x2e\x93\x23\x8a\x2d\x93\x63\x7d\x47\xf9\x75\x9a\x1c\xab\x1c\xe5\xd8\x69\x72\xec\xf2\x11\x93\xa3\xd2\x62\x8d\x00\x04\x98\x1c\x43\xe7\x47\xc0\x02\x4c\x8e\x31\x32\xc6\xc0\x46\x4c\xce\xf5\x43\x1a\x33\x39\x80\xca\xa8\xc9\xc9\xcb\x43\x15\x6a\x6f\xf6\x69\x82\xee\x5a\x2d\xd7\xfb\x87\x8c\x98\x54\x75\x9d\x62\x5f\xa0\xea\xb2\x3c\x53\x16\x88\xa1\x2a\x20\x67\x95\x6f\x14\x5f\xd1\x88\xc5\x6c\x9f\x64\x2b\xcc\xa8\xaf\xb7\x64\x9f\x8e\x37\x22\xc4\xaa\x28\x7e\xc6\x0d\x0a\xab\xc4\xb0\x26\xda\x37\x44\x8c\x0e\x3b\xa2\x95\xd9\xa2\xc5\x2d\x88\x5e\xa2\x9b\x8f\xbe\xcc\xb2\x1d\xfa\x47\x9b\x3b\xa7\xd5\xd0\x0b\x6d\xfe\x2c\x7b\x81\x16\x29\x0e\x5d\x23\x8d\x67\x41\xf3\x95\x06\x98\x09\xa8\xad\x3e\x98\x00\x03\x01\xb4\xda\x4f\x6a\xcc\x34\x5c\x37\xfa\x50\xbb\x30\x90\x18\xb5\x0b\xf2\xf0\x46\xd8\xa8\x3a\x24\x24\x5b\x62\xcc\x51\x4a\xe6\x8b\x35\x42\x58\x57\x1c\xf9\x11\x76\xbc\xcc\x11\x66\xb8\x22\xf2\x33\x06\x3a\x6e\x29\xf4\x0c\x6b\xe3\x0d\xa3\xe9\x76\x33\xc3\x16\x9c\xd9\xea\x61\x35\x9b\x87\x36\x2c\xc4\x68\x38\x93\xbf\x39\x2c\x87\xac\xcf\x30\x1e\xe6\x67\x5c\xd0\x0e\x13\x62\x16\xef\xbc\xc8\x58\x1f\xb8\x6c\x89\x28\xb6\xcc\x89\xf5\x1d\xe5\xd7\x69\x54\xac\x72\x94\x63\xa7\x2b\x62\x97\x8f\xb8\x22\x2a\x09\xde\x08\x40\x80\x8d\x31\x74\x7e\x04\x2c\xc0\xd2\x18\x23\x63\x0c\x6c\x2c\xce\x72\xf5\x90\x46\xa3\x2d\x03\x95\x51\x93\xc3\x93\xb7\x05\x5a\x9c\x6c\xbb\x5a\x2c\xd1\x81\xb9\x5c\x1c\x16\xc4\xa6\x6b\xc4\xef\xf8\x37\x2d\x50\xc8\x73\xc7\x21\x60\x66\x70\x0e\x66\xa3\xf3\x46\x4e\xaf\x69\x50\xba\x5d\x24\x73\xcc\xf5\x23\xe9\x7c\x3b\x5f\x85\x35\x28\x28\x9e\xeb\xc8\x03\xe8\x0a\xe7\xf2\xca\xcc\x68\xae\xfe\x15\x15\xae\x2b\x96\xab\x97\x62\x02\x77\x44\x72\x8d\x32\x23\x90\xcb\x4a\xed\x38\xae\xf1\x19\xe3\xd4\x1d\xc5\x35\x8a\x31\x5e\x1d\x6e\x8b\x55\x38\xea\xb8\xc8\x2c\x88\xfe\xf2\x90\x00\xae\xa6\xdb\x7e\xa8\x90\xf0\x2d\x1c\x03\x63\xe4\x46\x8c\xca\xd5\xa3\x16\xb3\x29\x80\xc8\xa8\x4d\x29\xf2\x92\x6f\xd3\xa3\x37\xbc\x5d\x81\x1e\xb9\x27\x9a\x98\xa4\x44\xe7\xf6\x3f\x75\x45\xea\xbf\xec\xac\x2f\x40\x1b\x3d\x02\x56\x3c\x7a\x9f\x56\xb9\x76\xfb\xcc\xc3\xba\xcd\xa8\xa6\xba\xec\x03\x48\xa1\xa9\xf5\x94\xfd\xd8\x4b\x10\x11\xb5\xca\x5a\xac\xe6\x9b\x14\xdd\x65\xbd\x94\x19\x6d\x8a\xdc\xda\xd7\x1e\xaf\xd8\x31\x04\x8d\xa2\x91\x99\x1d\xb4\x60\xa4\xaf\x90\x66\xc1\x17\x6b\x3c\x3b\xc7\xea\x90\xc7\x53\xff\x97\x64\xf3\x08\xcf\xf9\xfc\x2a\xf7\xe8\x40\xdd\xed\x19\xd6\xad\xdf\xeb\xbd\xef\x4d\x54\x50\xe9\x4b\x0b\x2b\x7d\x69\x8d\x06\xf3\xfd\xeb\xbb\xd5\xe9\x3b\x71\x6a\x27\x2b\x1b\x50\x3e\x19\xd8\xc6\xe9\x54\x3b\xc7\xcc\xfe\xd2\x75\x55\xf9\xd3\x80\xa6\xdf\xab\xa7\x2d\xed\x5c\x85\xed\x65\x7f\xce\x61\xa9\xb0\x99\x08\x7f\x07\x92\x71\xfb\xa7\x36\xc3\xb5\x43\x04\x30\xfd\x93\xdc\x6f\x9f\xce\x56\x6d\xd4\x0b\x8e\x67\x7d\x35\x32\x4e\x39\xa0\x46\x40\x20\x3b\xd3\xbc\xd4\x39\x52\x59\x80\xd2\xaa\x28\x48\xdd\x52\x5d\xfc\x70\x18\x48\x08\x49\x03\xcd\x3d\xd7\x35\x6e\x38\x91\x67\xb1\xfa\x2a\x61\xf7\x55\xf6\x1a\x00\xce\x75\xd1\x60\x43\x2e\x71\xf1\x63\x85\x52\xed\x98\xc0\x65\x9e\x5d\x98\x80\xd7\xee\x84\xb8\xcb\xcf\x79\x79\x8c\x0f\x97\x52\x1c\x15\xa2\xa4\xa5\x56\x2f\xb8\xc1\x82\x48\xd9\xd5\x66\x17\x69\x73\xa6\x0b\x24\xcf\x18\x52\xee\x47\xb6\x6b\xa8\x9b\xaa\xa6\x4d\xdf\xdb\x5c\x2c\x93\xe8\x39\x6f\xf3\x7d\x5e\xe4\xdd\xab\x5d\xdf\x18\x74\x20\xa8\xea\x2e\xd2\xd0\xce\x7f\xe8\x0c\xa6\xc5\xd4\x3a\x4e\x7f\x6c\x8d\x5b\x15\xf7\x49\x30\xf8\x2c\xd3\xb2\x7e\x89\x32\xd2\x9e\xf8\xd9\x16\x3d\x5d\xe9\x72\xe4\x5c\x95\x78\x47\x0e\x83\x92\x8d\x92\xde\xf9\x24\x62\x3f\xc5\x21\x4a\xf7\xad\x5c\xc3\x9b\xc7\x4e\x52\x25\x16\xec\x99\x96\x97\xb1\xdb\xb7\x32\x57\xa0\x38\x3e\xfb\x08\xef\xdf\xce\x12\xee\x2c\x69\x83\xf9\x71\x78\xff\xa6\xc7\x79\xd4\x33\x93\xce\xd6\x58\x86\x03\x2d\x43\xe9\x7c\xc8\x60\x89\x9c\x01\x34\xdf\x76\xe3\xf3\x40\xdb\xc9\x14\xd7\xc8\xe9\x37\x63\x81\xa5\x3c\x25\x00\x51\xe4\xf5\x2e\x1a\x32\x66\xbc\x98\x14\xd0\xf2\x91\x3c\x85\xb0\xc4\x3c\x7f\x25\x4f\x6c\x85\xe6\x33\x4c\xd4\x71\x49\x8b\x14\x72\x07\xc7\x0f\x8d\xaa\xc1\x94\xbd\x4c\xca\xf4\x17\x1e\x5d\x4d\x86\xae\x87\x17\xd8\x75\x0d\x9a\x66\xf9\x73\x2e\x53\xd0\xa9\xe9\x18\x9e\xec\xdc\x45\x5b\xd9\xcb\x98\xa9\xc4\x82\x6b\x30\xbf\xab\x5e\xdf\x53\x91\x3f\x11\x7f\x62\x4d\x36\xef\xab\x24\x41\x69\x41\x49\xc3\x1e\xac\x3b\xdd\x70\x8a\xd4\x38\x70\x85\xa6\xf8\x76\x71\xa9\x7c\x4e\xa4\x08\x71\x80\xd7\xfd\x3f\xa7\xb3\xe8\xd2\x6a\xed\xdd\x23\xbd\x22\xf9\xda\x12\xb1\x58\x50\x25\x0e\x16\x87\x72\xb8\x6c\x33\x46\xd2\x35\x6c\x0e\x0b\xa9\x11\x83\xf4\xa4\x56\xf1\x18\xdb\x43\x19\x88\xbc\xb8\x61\x50\x77\x7c\xb4\x5a\x97\x4c\x50\xc2\x4e\x21\x38\x8e\x41\x8e\xae\xe4\xdc\x47\x45\xe5\x09\xca\xba\xa9\x8e\x79\xb6\xfb\x2f\xff\xf3\x8f\x7d\xf9\x9f\x7b\xf4\x43\xd5\x9c\xa7\x7f\xca\xd3\xa6\x6a\xab\x43\x37\x3d\xf6\x36\x85\x96\xdd\x07\x5a\x32\x8e\x7f\x38\x90\xa2\xa5\x6a\xe8\x1b\x11\x20\x35\x0f\xa0\x0e\x17\x87\x26\x21\x93\xc9\x6d\x06\x84\xcd\x87\x10\x49\x5e\x79\x32\xf2\x06\x29\xa4\x13\x25\xd2\xdc\x04\x9a\x80\xf1\xc5\x03\x36\xe4\xc5\xd2\x2d\x64\xc8\xf7\x9d\xd6\xff\x61\x4c\xa7\x87\xfc\x85\xf7\x3a\x9e\xc9\x42\x3b\xf0\x8e\xcd\xb0\xdb\xad\x6a\xfd\x60\xa0\xb1\x9e\x1b\x17\xf7\xa5\x8e\xb8\xab\x34\x89\xa6\x25\x79\xde\x93\x26\x66\xdc\x89\x53\xf7\x83\xb2\x47\xc0\xa3\x4a\xab\xb2\xa3\x65\xb7\x8b\xde\xbf\x37\x1d\x20\x2c\x41\xb7\x72\x69\xcc\x8a\x35\x86\xc7\x19\xb0\xdb\xc7\xaa\x94\x7a\x31\xdc\x00\xc4\xdf\x6a\x0d\xbe\x6e\x21\xd8\xe0\x5a\x8b\xd4\xaa\x89\x15\x79\x8f\xd9\x83\x3f\x28\xb5\xa9\xd5\xef\xec\x27\x74\x87\xf5\xb0\x58\x07\xf3\xb5\xb1\xf4\x44\x6f\xc9\x97\xe7\xba\xd7\x65\xae\xbf\x25\x1c\x5c\x85\x8b\x3b\x70\xc3\x39\x6f\xac\x66\xe0\xe3\x8d\x50\xd5\xc3\x61\x08\x80\xb9\x8f\x87\x81\xc0\xe8\x0d\x56\xae\x47\x7c\x87\x16\x20\x95\x6b\x75\x5a\xdf\xf5\x30\x2f\x28\x18\x4c\xfe\x90\x80\xc6\x6a\x39\x23\xf7\xc9\x94\xe5\xf0\xd5\xee\x65\xf0\x93\xe3\x69\x55\x9b\xa5\xf6\x8d\x2a\x79\x75\x4a\x0f\x74\x74\x55\x55\xec\x49\x83\x00\xae\x70\x40\x8b\x39\x55\x60\xde\x04\x75\x74\xbc\x80\x87\x7a\x04\x3f\x61\xb4\x9f\x2c\xda\xfa\xab\xdc\x48\xbc\x88\x77\x45\x59\x75\xd1\x07\xed\xe1\x8e\x8f\xe2\x1b\x78\x47\x42\x7c\x32\x97\x46\xdf\x7e\xf3\xee\xe3\x2f\x23\xf1\x5c\xa8\x0e\xc6\xeb\x20\xe6\x6d\xc2\x31\xa4\x60\xbe\x22\xc8\x55\x57\xd5\xdc\xaa\x0c\xfc\x59\xe6\xd6\x04\x70\xf0\x32\xd4\x8c\x89\x4d\xd7\x7f\x73\x15\x59\x56\xdd\xb7\xdf\xbc\x33\x50\x0c\x36\x7b\x49\xf8\xb8\xd4\xcb\x71\x26\xc7\xb5\xcb\x84\x0d\x54\x81\xc1\x88\x85\x75\xb7\x20\xee\xef\x3f\x53\xae\x88\xb1\x08\xa4\x63\xaa\xc0\xaf\xa2\x01\x82\x17\xbf\x1e\xe0\xca\x7e\xbf\x6e\x8e\x2c\xdd\xb2\xed\x2f\x73\x34\x2d\x40\x87\xdf\xa9\xb7\xf1\x13\x8a\x65\xe4\x22\x7b\xc0\x52\xff\x3e\xb8\x8c\x0c\x4b\xed\x15\x40\x75\x36\xc7\xc8\xce\xe6\x08\x5d\x4f\x03\xff\x2e\xf7\xbe\x7c\x5c\xe9\xdb\x4f\x37\xee\x2e\x41\x57\xd2\x6d\x38\xd9\xc5\x56\x05\x27\xd4\x4a\xc4\x8b\x56\xa2\x21\x88\xc6\xc9\xa0\x9e\xe9\x6b\x8e\x91\x4c\x24\xd1\x2b\x9c\x21\xbd\x44\xcc\x65\x1e\x77\xc5\x74\xa1\xec\x65\x0a\x78\x22\xfa\xd1\x7e\x99\xc3\x78\xae\x66\x84\x53\xb3\x36\xe4\xf9\x69\x07\xa6\xe5\xa9\x20\xc5\xa1\xad\x1d\xa1\x65\x39\x31\x22\xa6\xa0\xa5\x90\x9e\x21\xb7\xc2\xed\x21\x6f\xb8\x7e\x61\x53\x41\xd0\x24\x60\x3a\x9d\x5e\xfb\xed\xb5\xd6\x22\x98\xe7\xb5\xd7\x57\x5b\x51\x83\x3d\xbf\x49\x0f\x35\xe0\x41\x33\x8d\x86\xbd\x1c\x1d\x3b\xd7\x4f\xd2\xf2\x60\xc0\x75\x3d\x74\x87\xf9\xfa\x26\x82\xb6\x4f\xf9\xcb\xaf\xdc\xd9\xb7\xcd\xe2\x3a\x63\xd7\xaa\x81\xcd\xd2\x5f\x2f\x6d\x97\x1f\x72\x9a\x21\x1b\x69\x88\x19\xe3\x1b\x6c\x05\x79\xad\x2e\x1d\x08\x86\x0c\xc7\x06\xd8\xbe\xdc\x2e\x6a\x69\x4d\x1a\xd2\x21\xd6\x4a\x55\x68\x9b\x64\xbd\x08\xb8\x90\xe3\x8f\xf2\x43\x56\x11\xe3\x8a\x52\x56\xd6\xd5\x6b\x97\x71\x54\x3b\xb2\x60\xc5\x68\x7e\xcc\x48\x47\x84\x3a\x89\xdd\xe3\xf6\x27\x6e\xd2\xf1\xdc\x28\x61\x08\x43\x56\x78\x37\x3c\x9c\x3e\xae\xad\xcb\x81\xab\x32\xb3\xb8\x77\x97\xf8\x9e\x4a\x43\xd3\x6e\x70\x50\x12\xe6\xd0\x8c\x67\x5d\x1d\xfa\x7a\x24\x2a\xa2\x14\x73\x54\xe5\x00\xe1\x1f\xd3\x82\xb4\xed\xef\x7f\x48\xab\x22\xfe\xc9\x9c\x50\x1f\x6f\xc9\x75\x7e\xb4\x13\x1e\x79\xb8\xd7\xf3\xdb\x1a\x7b\x69\xfe\x47\xec\xb0\x7a\xd9\x99\x12\x23\xd7\x92\x59\x8c\x66\x58\xf2\x42\xed\xbb\x72\x70\x38\xfe\x6e\x59\xb0\x5d\x0d\x74\x00\xa1\xcd\x0c\x80\xf5\x36\xf6\xca\x94\xdb\x7e\x96\x3d\xe0\x1e\xe6\x03\xb1\x64\x33\xac\x9c\xdf\x0e\x9e\xc4\xee\x06\x0a\x68\x73\xe3\x26\x8a\xf1\xe0\x49\x8f\xad\xbf\x2a\xe4\xd5\xdd\xbe\x78\x5c\x77\x4d\x28\xb4\x3b\x7f\xbb\x07\x08\x5c\x2d\x73\x00\x85\x2a\xed\x55\xad\xbc\xf2\xb5\x03\x3f\xcb\x1e\xf0\xeb\x95\xd6\xd5\x0c\x87\x7e\x59\x3c\xb9\x94\x16\xe7\xc6\x4d\xf4\x76\xa5\xb5\x32\xdb\x21\xd5\xa2\x29\xea\x10\xc7\x65\x94\x7c\xa0\x8f\x6d\x8d\x09\xc6\xc7\xad\xc8\xf7\x59\x75\x85\x8a\x45\x79\x6a\x8e\xcd\xb8\xf1\xed\x0e\x3c\x1f\x67\xe8\x73\x97\xe3\x07\x06\x1e\xed\x97\x2f\xf1\xe4\x53\xee\x57\xf3\xae\x79\xf5\xd2\xd9\x2c\xf4\x49\x96\x51\x4b\xe6\x3e\x32\xe9\xaa\xe0\xda\x73\xa9\xee\xd3\xa7\x76\xcf\x38\x32\x01\x7a\x01\x35\x77\x53\xcb\x80\x37\x3e\x7e\xe0\xe3\xe1\x48\x35\xe6\xe3\xe2\xd6\x38\x00\xe5\x72\x8d\x32\x06\x02\x9c\xe6\x71\x92\xc6\x52\x13\x21\x0f\xde\x8c\xd6\xd7\xde\x2c\x76\x6f\xad\x84\xc7\x29\x58\xe3\xf8\xdb\x6f\xde\xfd\xda\xbb\x16\x5e\xc1\x6b\x11\x7c\xf5\x24\xf8\x58\xcf\x82\x15\x3f\xd6\xb1\x46\x40\x20\xac\xd3\x9c\xab\xee\x20\x1a\x8e\x1d\x98\x91\xee\x40\x49\xa3\x9d\x1e\xd2\xbf\xde\xd0\xc5\xed\x91\x7e\x9f\x7c\x61\xf7\xb9\xd7\x47\xf1\xc8\x7e\xf3\x60\x56\x92\x91\x53\x19\x4e\x2f\xc8\x7d\x7e\x12\x43\xf9\xa4\xf0\x9c\x5b\xad\x18\xda\xb0\xb3\x81\x96\x0e\xfb\xcb\x68\xb1\x6f\x97\xf9\x6e\xa6\x47\xb7\x94\x2a\x27\xa7\xb3\x5d\x96\x46\x06\x1a\x21\xbd\x1e\x5b\x7c\x25\x79\xd6\xb6\x54\xc0\x11\x18\x2c\x25\xa4\x79\xce\x13\x90\x79\x2a\xf2\x90\x78\x80\x76\xae\x89\xa3\x89\x93\x4d\xa1\x8f\x53\x1a\x73\xde\x4a\x6b\x8c\x7e\xe8\x4f\x7d\x08\x38\x2c\x86\x3b\x06\x3a\x69\x70\xfe\xcc\x75\xa4\xcd\x02\x34\x98\x19\x3d\x1b\xe7\x3d\xd6\x77\xcb\x89\x36\xd8\xd5\xe2\x24\x19\xe7\x46\xfe\x05\x39\x54\xdf\xc0\x15\xa3\x11\x8f\xc9\x38\x60\xa8\x55\x57\x92\xe7\xf8\x57\x3f\x9b\x2a\x7b\xf9\x29\x3f\x1f\x85\xb1\x50\x7b\x37\x86\x92\xc6\x1d\xd9\x6b\x89\xed\xd5\x41\xa6\xc1\xe9\xcb\xb2\xcc\xc4\x90\xba\x6d\x9e\xaa\xd6\x47\x88\x31\xae\x24\xa6\xd0\x15\x7d\xa8\x8f\x1f\x81\xbb\x21\x45\x28\xfb\xff\xc9\x60\xda\x35\x26\x9c\x97\xc6\xfa\xde\x14\xff\xc1\x9b\x0e\xcf\xb0\x62\x9f\xa1\xfe\xd8\x85\xd8\xe9\x55\xe1\xa1\x4b\x6d\x16\x09\xe1\x5c\xc7\x6b\x61\x4e\x39\xb3\xa7\xec\xc9\xd1\x3b\x00\x18\x7b\xec\x87\x1e\x47\x37\x62\x7a\x86\x72\x58\x02\xd5\x29\x18\xda\x81\xa9\x9c\x85\xa0\x2b\x85\xac\x68\xe5\x7a\x0a\x6b\x84\x5e\xf8\xc1\x3d\x2b\xea\x1d\x70\x40\xcf\xd3\x64\x57\xa4\x1f\xc6\xf6\xd5\x41\x3d\xaf\x24\x90\x38\xea\x70\x38\x2f\x50\x8c\xfa\xc1\x4e\xd7\x9a\xcd\x25\x43\x44\xcb\x5d\x20\xc0\x85\x18\x05\x35\x87\x9d\xc3\xd6\xdc\xdc\x0f\x52\x7e\x5e\x83\xf6\x08\x20\x50\x83\xe1\xed\xa4\xdb\x44\x73\x95\x6c\xf0\x41\xcc\x2f\x0c\xeb\x7a\x50\xe7\x45\x81\x19\x64\x0c\x46\xc8\xc6\xaf\x0b\x12\xf8\x93\xa0\x69\xde\x48\xc2\x60\x4d\x89\x58\xdf\x35\x9b\x68\x97\x7a\x8e\xf4\x3b\x4f\xee\x43\x3e\xda\x8e\xa4\x5f\x46\x2d\xcf\x00\x65\xb4\x8d\xbf\x28\xe2\xdb\xe9\xf7\x9a\x48\x14\x68\x84\x97\x3b\x19\xbf\xdf\xcc\xe6\xbd\xd1\xd4\xdd\x6a\xe1\x0c\xa1\x8f\x4d\x44\x77\x33\x86\x23\xa3\x3d\xd0\x08\xfe\x16\x06\xf0\x57\x37\x7e\x6f\x12\x45\xb0\x2c\xc2\x0c\x5e\x47\xf6\xb1\xb8\x01\xf0\xc4\xfe\xa8\x49\xe9\xb9\xbe\xab\x81\x83\xdc\xe7\xce\xd5\x17\xf7\x87\x91\x01\x84\x9d\x09\x7a\xe3\x19\x06\x7e\x54\xdf\xb3\xd4\x83\x0f\x5b\xac\xf0\xe7\xe5\xe5\x6d\x12\xaf\x73\x7c\xd5\xd5\x03\xad\x33\xb4\x01\x12\xfc\x80\x83\xb8\x83\x00\xee\xc4\xe8\xf3\x92\x36\xca\x7b\x48\xed\x1e\xb6\x79\x98\x71\x85\x1e\x66\x14\x5f\xe5\xfa\x28\x7e\xd9\xf1\x7b\xb8\x85\x76\xe5\x58\x15\xb7\x69\x53\x15\x05\x5b\x25\xb3\xa7\x11\xcc\xab\x23\xce\x45\xc5\xd8\xb3\x5e\x09\x3f\xd0\x38\x5f\xad\x26\xd1\xf0\x9f\xe9\xcc\xfb\x0a\x99\x13\xc9\x21\x17\x75\x83\x5d\x36\xe7\xf5\x5a\xf3\x6d\x49\xd9\x7a\xcb\xc9\xba\x48\xe3\x3d\x61\x89\x1c\xb1\xd4\xee\x9f\x28\xce\xf5\x4a\xf5\x71\x17\xfd\x53\x7e\xae\xab\xa6\x23\x5c\xd4\xda\x26\x96\x59\x66\xbe\x1d\xcf\x59\x1c\x96\xc7\xa2\xf3\x01\x9a\x8b\x23\x21\x4b\x4d\x98\x02\xdb\x40\xd1\xef\x02\x19\x74\xcc\x2b\x43\x5d\x55\xdb\x30\xd2\x00\xf6\x1f\xf9\xa3\x57\x28\x1c\x67\x08\x3b\xc3\x81\x3f\x59\xa4\x29\xc9\x9b\xb9\x54\x21\xb1\x17\xf0\x06\x71\x82\xdc\x58\x22\x2f\x71\x46\x9f\xf3\x94\x4a\x25\x5b\x3e\x24\xf5\xcb\x47\x52\x66\xd1\x87\xaa\xc9\x69\xd9\xf1\xe8\x4c\x41\xca\xac\x4d\x49\x4d\x75\xfd\xbb\x07\xa3\xef\x84\xe3\x30\xb0\x3a\x4f\x12\xfd\xbd\x97\xde\xde\x93\xbc\xa4\x4d\x7c\x28\x2e\x79\xf6\x84\xd4\xe4\x02\xe1\x16\x8b\xcd\xe0\x0a\xc4\x8b\xff\x84\xd8\xba\x7b\x3f\x2b\x74\x97\xf6\xbc\xa5\x41\x98\x03\x85\x3e\x61\xa5\xa9\x25\x50\xf7\x5f\xb0\x1b\xfa\xe6\x09\x68\x66\x19\xaf\x33\x68\x46\x15\xc8\xae\xae\x77\xb0\x60\x2a\xf7\x0b\x7e\x57\xd2\xbe\xcb\x68\xa6\x1d\x58\x24\xd7\xf1\x1e\xc2\x48\x78\x83\x24\x86\x75\x0d\x12\x15\x2f\x3a\x20\x7f\x31\x2f\x7f\x62\xe1\x6e\x9d\xea\xcc\x8a\xae\xf5\x54\xf7\x4d\x6f\x10\x90\xe8\xa0\xe9\xca\x0c\x71\xeb\x95\x88\x5b\x87\x1c\xc9\x9a\x27\x56\x3b\x58\x8d\x70\x81\x39\x7c\x1c\x89\x73\x63\x84\x54\xd4\x14\x75\x12\xc3\xba\xf7\x69\x18\x51\x3a\x43\x8a\xbf\x27\x73\xd0\xea\x70\xc0\xd4\xd9\x66\xc3\x52\x02\x78\x15\xc4\x77\x33\x92\xa9\xb1\x26\xfa\x2d\xd8\x87\x87\x2e\xee\x83\xf6\xc9\x70\xc5\x0c\xcd\x90\x3b\xea\xb7\xdf\x22\x7f\xc3\xeb\x4f\x9a\x04\xbc\x39\x46\x74\x59\x4d\xf3\xb4\x2a\x63\xe9\xef\x3a\x53\x2f\xcd\xe7\xfa\x5b\xf6\xf8\xf9\x04\x7b\x68\x99\xb5\x7c\xd2\xeb\x83\xa2\x5e\x5e\x6b\xf6\x40\x6f\x9b\xab\x1e\x4b\x33\xe4\x76\x96\xdc\x58\xd8\x4c\xfb\xb1\x16\x1b\x9b\x44\x02\x12\x6c\x3d\x69\x2f\xb8\xe9\xe3\xd5\x7a\xed\xd4\x35\x3c\x35\xc7\x41\x35\x67\x63\x36\x47\x6d\xb2\xa0\xd7\x9d\x07\x7d\xe6\xd6\xfe\x11\xac\x26\x94\x33\x6a\xfa\xb2\xf6\x43\x93\xa3\xca\xa9\x74\xf0\xed\xbe\xaf\xb3\x41\x66\xba\x80\xc1\x58\xb9\x50\x58\x8f\x0c\xaf\x49\x6a\xa7\x67\xe4\x3d\xa4\xb9\xb2\x09\xa3\x1c\x80\x00\x01\xd2\x69\xe1\x24\x4c\x5b\xeb\x07\x96\xb1\x3d\xde\x08\x57\x0e\xcf\x6b\x57\x97\x52\xb3\xcd\x59\x46\xa9\x7a\x62\xb7\x48\x85\xae\xac\xe5\xe8\x3b\x74\x18\x18\xe3\x40\x98\x3f\x7b\x20\xe0\x56\xf9\x50\x35\x67\xec\x50\xd2\x2a\xd0\xdc\x9a\xae\xa3\x61\x6f\xed\x99\x21\x78\x51\xeb\x8c\x0e\xdd\x6b\xf1\x3b\x91\x00\x2c\xd9\xc0\x1d\x17\xc8\x21\x84\xaf\x74\xc4\xfa\x5e\xfa\x75\xdf\x5b\xb6\x6b\xfa\x55\xde\x5b\x76\x56\x13\xfe\xde\xb2\x46\xe2\x6e\xef\x2d\x3b\xa9\x9a\xa7\x52\xdd\x80\x8e\xf7\x96\xc3\x10\x7c\xef\x2d\xbb\x28\x04\xbe\xb7\xac\xa1\xdf\xe7\xbd\x65\x9d\xe4\xf0\x02\xae\xf6\xfd\xb7\x7b\x6f\x19\x65\x47\xbd\xb7\x8c\x30\x35\xfe\xde\x32\x4e\xd2\x71\xca\x12\xa9\xe1\x4e\xef\x2d\x6b\x94\x6f\x7f\x6f\x39\xd0\xcd\xc1\xed\x8c\xbd\xe5\xe3\x1e\xcd\xe6\x75\xbb\xd1\x4d\x94\x6b\x2c\x20\x1a\x13\xd4\xa6\xbf\xc4\x13\x87\x1b\x8f\x10\xdc\xc7\xc7\xc2\x3c\xd6\x91\x60\xbd\x19\x8c\xbf\x3d\x52\x8f\x07\xb0\x46\xb9\xc0\x16\xd0\x16\x23\x4b\x6b\x47\xe1\xd7\xbb\xbb\x2b\xd7\x99\xfa\x49\x42\xc4\x0f\x01\x0e\x87\x8d\x3b\x05\x19\x79\x21\x89\x19\xbe\x51\x31\x43\x96\xed\x82\x88\xc8\xb0\xab\x11\x59\xe2\x44\x90\x85\x1f\x7d\xe9\x6c\x74\xdc\x71\xba\x22\xe8\x66\x52\x77\x78\x99\xe6\xca\x58\x57\x7a\xc3\x2f\x0c\xac\x73\xc8\xd2\x04\xea\x74\xc7\xb5\x87\xfc\x66\xda\x2a\xdf\x8c\x9f\x23\xae\x25\x4a\xe8\x7f\x23\x74\x8d\xd1\x8d\x0c\x45\xf8\xc2\x2d\x76\x62\xe9\xa1\xff\x87\x9c\x90\xa3\x9b\xfe\x9f\x83\x98\x1d\x50\xc2\x8f\x17\x3a\x71\xcc\x65\x0a\x0e\x84\x9e\xc7\x62\x07\xea\xae\x39\x49\x88\xd1\x57\xda\x13\xce\xb7\xb6\xfc\xb8\x1e\x6d\xac\xc5\xfa\xf9\xcf\xd0\xf7\x7b\xc3\x5b\xdc\xd3\x37\xf6\x88\x47\xc1\xf4\x6d\xf3\x71\x70\xfb\x90\xa8\x38\x3d\x87\x9d\x8b\x0c\xd1\x2f\x46\x5e\xcb\xf0\x18\x06\x18\xc8\xb8\xf7\x7c\xab\xbc\x50\xf3\x16\x2d\xb3\x12\xf5\xa8\xa7\x07\xb4\xd3\x8b\x6e\xdc\x31\xad\x11\x50\x63\xaf\x25\x04\xd6\x66\x84\xde\x10\x4a\x0f\x0f\x0f\x23\x94\xec\x5d\x23\x13\x42\x39\x35\xb7\x58\x1c\xd6\x6f\xf0\x64\xf0\x08\x50\xa0\x26\x58\x27\x89\xaf\x54\xe0\x40\x4f\x13\xa9\x7b\x2c\x1a\xa4\x59\x1a\x6d\x56\xb8\x8a\x98\x71\xec\xe3\x7a\x7c\x70\x1c\xc4\xb0\x4a\x63\x43\xe4\x36\xa6\x07\x5b\x75\x13\xcf\xae\xf3\x2e\x37\x12\x41\x5a\x2f\x34\xc3\xa7\x1a\x37\xb6\x1c\x18\xbc\xdb\xd8\xc6\x0c\xe1\xdb\xc8\x20\xed\x17\xf6\x31\xa4\xf7\xbd\x23\x5a\xa5\x8d\x0a\x9f\x53\x1d\x8f\x67\x80\x77\xb7\x4d\x44\x2d\x3f\x55\x40\x45\xd6\x53\x24\x4e\x80\x9b\xd8\x40\x1e\x1a\x09\x00\xf5\xbe\xfc\x33\xd6\x82\xab\x70\x30\x1f\x04\xbc\x8c\x2e\xf0\xf2\xf2\x99\x36\xe2\x88\x04\xf6\xe0\xf7\x7c\x8e\xf8\x95\xc9\x43\xff\xcf\x41\xc9\xed\x57\x6e\xb3\xfe\x5f\x08\x9a\x29\x51\x1c\xe8\xaa\x53\xad\xee\x19\xdf\x24\x6e\xf9\x95\x41\x7c\xa3\xae\xe5\x55\x98\x63\x8d\xf6\x7a\x97\x77\x68\xb7\xc3\xbb\xf4\x82\xd9\x53\xb3\x1f\xdc\x1e\x69\xee\x63\xc8\x41\x5a\xe6\xf4\x2e\x47\x00\x03\x19\xf7\x7a\x97\x4b\xf1\x2e\xf5\x5b\x74\xcd\xe9\x5d\xda\x16\x08\xc7\x1d\xd3\x9a\x30\xef\x32\xb4\xb6\x71\xef\x12\xbc\xb9\xe5\xa0\x64\x7b\x97\x26\x84\xcb\xbb\x9c\x25\xfd\xbf\x10\x8d\x30\xbd\x4b\x0f\x50\xa0\x26\x38\xbd\xcb\x40\x05\x0e\xf4\x2e\x91\xba\x5d\x33\x3b\x92\x1c\xdd\x65\xa8\x35\x27\x26\xb4\x0a\xfd\x99\x07\x5f\x03\x6f\x22\x8f\xf8\xc7\xd2\x5c\xde\x4e\x0f\xf1\x95\xae\xc3\x47\x9c\x24\xd1\xc1\x57\xb9\xc8\xe1\xdd\x88\xba\xc8\xd7\xa3\xbf\xa5\xe1\x5e\x17\xd9\xdd\xfa\x37\x76\xbf\xcb\x45\xbe\x85\xc0\x9b\x5a\xef\x77\x91\x85\x91\xbf\xda\x45\x36\xeb\xb7\x3c\xd7\x20\xdf\xc0\xe1\x9e\x7a\x8c\x2c\xea\x25\xfb\xeb\x72\x39\xca\x36\xc0\x4d\x9c\xb8\x1d\x65\x1f\x68\x88\xa3\xec\x6c\xc1\x55\x38\x98\x3b\xb5\x5c\x2e\x65\xb3\xf6\x0d\x25\x59\xda\x5c\xce\x7b\xfd\xb4\xc1\x83\x7d\xd8\xc0\xbc\x36\x10\xfa\x4c\x11\x7b\xd0\xc5\x7f\x10\x6b\xe0\x42\x9e\xb2\x70\xec\x34\x23\xe0\x9f\x8a\x7c\xb7\xa7\x87\xaa\xa1\x7a\x0b\x12\x79\x07\xca\x58\x0e\x0e\x6f\x40\x7c\xfe\x4b\x92\x90\xe4\x3d\x42\x15\x5e\xf7\x40\xd6\x62\x35\x39\xe6\x25\x3b\x09\xe8\xe6\xd5\xbe\x76\xa0\xbf\x0a\x95\x18\x59\x80\x11\xa9\x0c\xd5\x38\xa4\x82\x02\x32\xc7\x40\xff\xd2\xd6\x24\xf0\xe9\x83\x47\x57\x1e\x21\x2b\xe7\x81\x75\x78\xcb\xf5\xcc\x90\x7c\x27\xe7\xaa\x47\x80\x7c\xb7\x94\xd1\x56\x6b\x99\x21\x2c\x09\x68\xa5\x4a\x1a\xf6\x26\x63\xd8\x0e\xdb\x48\x0e\x59\xbd\x66\x90\x48\xc2\x66\x0b\x14\x2a\xae\xde\xb2\x8d\xe7\xe2\x02\x2c\xbf\xcc\xef\xf2\x6d\x67\x5b\x63\x80\xeb\x88\x14\xfa\x1e\x3c\x0d\x4e\x72\x80\xf7\x26\x5c\xa6\x61\x9f\xb1\xb6\x18\xb3\xbc\xc9\xb5\x2c\xee\x59\xc7\x11\x41\x8b\xdd\xe5\xd0\x98\xea\x69\x2d\x0d\xcd\x0d\xbb\x8c\x6f\xbd\xc6\x8b\xdf\x51\x85\xdc\x68\x8b\x41\xbc\x00\x6d\x85\xe9\x06\x58\x02\x52\x00\x88\x88\xb4\x32\x5c\x2d\x0c\x18\x67\x32\x8e\xe0\x9c\x1b\xba\x0d\xf0\x6b\x4c\x5c\x1c\x6d\xc3\x27\x3e\x0e\xb6\x2f\x24\xbb\x97\x8b\xb6\xc7\xbc\x20\x00\xd8\x58\xd6\xac\xc5\x7a\xd4\x9e\xac\x3d\xbc\x38\x4d\x8a\x5d\x3e\x6e\x55\x50\x56\x2c\x10\x84\x97\xf6\x8c\xc8\x9c\x7f\xb4\x65\xee\x49\xd9\xe6\x22\xed\x13\xb9\x0d\x30\x2a\xf2\xc5\xa8\xc8\x17\x1e\x5e\xdc\x22\xb7\xca\xc7\x45\x8e\xb2\x62\x81\x00\x5e\xc4\x50\x0a\x72\x27\xf0\x44\x7d\xae\x54\x43\x9c\xf8\xa8\x77\xc1\x60\x64\xcb\xf9\x1f\xaa\x99\x63\xde\x8f\xe8\xfc\xa5\xeb\xa0\x7f\x70\x4a\x12\x75\x60\x7e\x65\x0a\xc7\x9c\xe0\xe4\xa7\x37\x27\x2a\xe2\xb4\xa6\x25\x7d\xe9\x40\xeb\xf9\xdf\x4a\x00\xf0\xe4\x84\x81\x58\x37\xf4\x39\xaf\x2e\x2d\x44\x56\xdf\x4c\x02\x30\xef\x82\x80\x35\xad\xbd\xfe\xcd\x68\xb2\xd3\xc6\x6b\x65\xaa\xd6\xb7\x18\x66\xc9\xe6\x70\x5c\xcf\xd0\x02\xad\xff\xa7\x73\x7a\x8e\xa6\xeb\xfe\x3f\x0b\x7a\x36\x4c\xc0\x66\xf5\xbb\x47\x33\x29\xe5\x66\x2c\x29\x25\x7c\xb0\xd1\xd2\xf5\xc0\x94\x9a\x7b\xd2\x52\xf5\x06\xbb\xae\x61\xd3\xf9\x8a\x9e\x45\x1b\x09\x6f\xa4\x94\xb5\xfc\xd3\x19\x29\x1b\xcd\x36\x25\xd2\x81\x6b\x22\xdc\xd1\x73\xdd\xbd\xea\x82\xb4\xde\x1e\x19\x84\x8d\xbb\xf1\xea\x7a\xb9\x46\x7a\xec\x78\x0d\x58\xd6\x68\xf0\x3f\x9e\x1a\x7a\x18\xd6\xb4\x58\x99\x37\xab\x15\x3f\x05\xa3\x93\xae\x9b\xfc\x4c\x9a\x57\x17\x8a\xee\xf5\x68\x28\x28\x37\x7a\x99\x97\x9b\xf9\xc3\x3a\x19\xde\x1e\xe4\xe8\xed\x25\x4d\x69\xdb\x3a\x1b\x90\xee\x1f\x56\x29\x8a\x82\x72\xa3\x97\x79\xb9\x59\x2e\xb7\xd9\xb0\x04\xe7\xe8\x79\x79\xa8\x9c\xac\xec\xd3\x24\xa3\x36\x3c\xca\x07\x28\xf0\x32\xb1\x98\xed\x93\x6c\xa5\x13\xfd\x4a\x9a\x52\xbe\x13\x8e\x8d\xfc\x84\x64\x4b\x8a\xa2\xa0\xac\xe8\x65\xfe\x24\x68\xe9\x76\x33\x3b\x18\x9a\x48\xca\xa3\x1b\x23\xdb\xae\x16\x4b\x14\x03\x57\x5d\x58\xe4\x65\x25\xdd\x2e\x92\xb9\xea\xf8\x3d\xc9\x8e\xd4\x3f\xd1\xc1\xf7\xa0\xcd\xeb\x89\x8b\xfa\x25\xda\x38\x53\xd5\xfe\x9d\xad\x1e\x6a\x0d\xb0\x19\x17\x9c\xbf\x64\xf2\x08\xb2\x57\x83\xe4\x02\xed\xd5\xf0\xec\xc4\x4b\xfb\x04\x68\x88\x67\x32\x5e\x5a\x48\x73\x38\x9f\x3b\x38\xf6\xda\x3b\x48\x44\xb0\xaa\x0c\x37\xff\xf3\x7e\x86\xbb\x77\xa9\x38\xbb\x79\x47\xcf\x72\x9d\xa8\x58\x1e\x72\x15\xa9\xb5\xe9\x13\xe0\x1f\x59\x03\xfa\xa7\x5a\xbd\x3a\x48\x0a\x73\x40\x70\xf0\x4f\x00\x4b\x3f\x98\xb9\xc2\x53\x33\xe9\x3c\x6b\x01\x13\xe0\xa2\xfe\xf5\x72\xde\x57\x5d\x63\xe6\xa1\x5e\x20\x37\x96\x64\x10\x51\x26\x6e\x17\x2d\xcd\xcb\x13\x6d\x72\xd7\x3a\x19\x78\x64\x43\x55\xd3\xd3\x6c\x12\x81\xbf\x4f\x33\x28\x57\x41\xd0\x46\xab\xb1\xe3\xd5\xc8\xfd\xe1\xf9\x0c\x19\xa2\xf3\x24\xb1\x28\x3e\x9d\x1a\xd3\xdd\x57\x26\x6a\xd5\xff\xb3\x72\x0b\x00\xae\xed\xfb\xf7\x91\x21\x4d\x77\xae\x69\x20\x8a\x81\xf4\x2f\xce\xd7\xb6\xc4\x16\x5d\x9b\x36\x94\x96\x11\xcb\xbb\x30\x18\x2e\x78\x94\x58\xaf\x7f\xe8\xce\xe5\x83\xb8\x3b\xf5\xef\x5a\x66\x81\xab\x5a\x63\xe6\xa9\x90\xcf\xe7\x1b\x6b\x9b\x35\xb8\x53\x38\xda\xe3\xb0\xd7\xd6\x0b\xfd\x36\x5d\x77\xba\x9c\xf7\x25\xc9\x0d\x27\xd5\x5e\xa3\xe0\x67\xc6\xe7\xd8\x2d\x55\x23\xb3\xe4\xdb\x17\x34\xc6\x43\xf9\x6c\xe7\x45\xd8\x4b\x0e\x19\x4d\xe7\x6d\x44\x49\x4b\xe3\xbc\x8c\xab\x0b\xbf\x5e\x57\x05\x02\x8e\x43\xd9\xc2\x62\xd9\x3f\x27\xd1\xf0\x05\x64\x03\x85\x56\x43\x5e\xf6\xd0\x0c\x03\x48\x2e\x43\x06\x0a\xea\xe1\x5a\xf0\x6d\xb0\xcd\xc3\x27\x67\x62\x4d\xdd\x3b\x1c\x78\x9d\xa6\xa4\x56\xa1\x78\x78\x37\xfd\x11\x3f\xf1\x44\x0a\xda\x74\x46\x44\xc8\xbf\xd1\xf1\x86\x1b\xe6\xbc\xb2\xd3\x12\xbf\x59\x82\xdb\x2a\x8e\xc3\xff\x67\xd8\xec\x32\x5d\x05\x0d\xfa\xa9\x9e\x08\x84\xa7\x4b\x81\xde\x1f\x31\xc0\x3f\xd5\x16\x47\x2b\x83\xeb\x38\xcb\xdb\x73\xde\xb2\x65\xa3\xa4\x2e\xbf\xb1\x6c\x39\xbf\xd8\xf9\x96\x16\x3e\x22\xd1\x34\x2d\xaa\x16\xa7\xc5\x8b\x46\x9d\x05\xe1\x36\xc9\x8b\x08\xd2\x46\x7b\xe4\xa8\x79\xf9\x4a\x1b\xd2\xcd\x7a\xe1\x5a\xde\x66\x87\x43\x92\x61\xf7\x0d\xb2\x35\xdd\xa6\x6b\x9c\xba\x7b\x0e\x48\xb7\x74\xbe\x5f\xe0\x58\x66\x1f\xab\xd5\xca\x7e\xb5\x1c\x3c\x50\x0e\xa4\xd6\x07\x83\xff\xbe\x49\x1e\x5c\x87\x33\xb2\x2d\xcd\x0e\x58\x64\x79\x9f\xd2\x87\xc3\x0c\x21\xed\x6e\x01\x59\xd3\x19\xc5\xb8\x71\xb2\xbf\x5c\xcd\xd7\x5b\x1d\x01\xae\x2c\xd4\x51\x6d\xb2\xce\x16\x7b\x97\x11\x4d\x0f\x0f\x74\x81\xb4\xe0\x40\xe8\x3e\x4d\x71\xea\xee\x46\x1c\x36\x74\xb6\x5f\xe1\x58\xae\x76\xac\xd7\xab\x99\xd9\x0d\x60\x4d\xa2\xe4\xb3\x5d\x2e\x97\x73\x57\x33\xe6\x19\xcd\xb0\xad\x8f\xbe\x11\xd9\x0c\x25\xee\x6e\x05\x5d\xee\xb7\x69\x82\x22\xb9\x1a\xf1\xb0\x5c\xac\x16\x72\xad\xf9\xcf\xdf\x7e\x23\x67\x99\x2f\xf4\xf5\xd0\x90\x33\x6d\xa3\xba\xa9\x8e\x0d\x6d\xdb\x78\xcf\xd2\xe2\x34\x79\x4d\xf9\x70\x39\x34\xd5\x39\xfa\x05\x34\x6a\x18\x9a\xcb\x84\xfb\x02\x8c\x6a\x67\x2d\x5c\x07\xc0\x21\xc5\xcb\xbf\xf3\xea\xab\xbf\x57\xcd\x7f\x8f\x6a\xa7\xb2\x2a\x06\x0f\x33\x26\x78\xa6\x9b\xc0\xb4\xdd\xbe\x7d\xf5\xc7\x80\xfb\xf7\x73\xe4\x3d\x5d\xef\xbd\x7a\x14\x41\x05\x2c\x81\x4c\xcd\xa0\xe6\xe3\x90\x12\x85\x4d\x7b\xca\x91\x12\x09\xa2\xc7\x5e\xa3\x9a\xeb\x6b\x05\xff\x3a\xd8\xbb\xcd\xe6\x93\x49\x0c\x72\x07\x80\x16\xba\x1f\x25\xf6\x22\x60\xce\x1c\x3f\x17\x37\x5d\x73\xe7\x0b\x71\xe2\x6c\x00\x77\x29\x26\x78\xa1\xcc\xd9\x24\x1a\x3e\x8b\x4f\x91\xdd\x43\x76\x32\x0d\xc9\x74\x2f\x7d\xd2\xc4\xc7\x5e\xa3\x68\xd9\x7d\x58\xae\x32\x7a\x9c\x38\xd2\x2a\xac\x3e\xf6\x3e\xf8\x7c\xf5\xbb\x09\x74\x8b\x22\xeb\xc3\x2a\xf9\x9d\x9b\x04\x2b\x75\xa7\x65\x58\x7d\x8c\x36\x26\x3d\xf3\xc3\xc7\x47\xbc\x4d\xd5\xb5\xcd\x61\xac\xb3\xdb\xda\xff\x80\xcd\xf9\xbf\xb7\x2d\x6a\xec\x0d\x6d\xe2\x63\x9e\x99\xd6\x65\x62\xed\x0f\x19\xa5\x98\xc2\xab\x45\x85\xfa\x2a\x4f\xee\xd8\xea\x2e\xeb\x27\x65\x7e\x16\x11\x1e\x74\x22\x98\xb7\x42\xc8\x51\x5e\x1e\xf2\x32\xef\xe4\x48\xbd\x0d\xf1\x7a\x2c\x7c\x64\x5f\x13\xac\xf6\x0f\x7e\x17\xad\xff\x34\x02\xff\x98\x03\xe7\x3f\x8a\x11\x40\xf5\x3a\x7c\xdb\x63\x44\xa9\x31\x42\xff\xa9\xd1\xff\x78\x5a\xf0\x1f\x5e\xa3\xaf\xda\x43\x1b\x51\x6a\x07\xad\xff\xd4\xeb\x7f\x3c\x5d\xf8\x0f\xaf\xd7\xd7\xec\xc6\x8e\xa8\x35\x4e\xea\x3f\xb5\xfa\x1f\x4f\x13\xfe\x23\x6a\x35\xdf\x07\x33\xc3\xdf\xf0\x54\x19\x83\xb0\x1e\xec\x44\x1f\x62\x65\xa0\x93\x88\xff\x6f\xbc\xaf\x32\xbe\x2b\x8e\xc5\x70\x7e\xae\xd8\x56\xa3\x86\x39\x60\x0c\x1b\x76\x49\x62\x70\x12\x57\xfb\xbf\xd2\xb4\x43\xb6\xb0\x74\x30\x16\x16\x97\xbc\x3c\x4d\xeb\x4b\x51\x80\x4c\x3c\xc6\x1b\x08\x56\x25\xfd\x77\x03\x59\x65\x13\x32\x9f\x55\xb0\x90\xfb\x66\x28\x29\x40\x4a\x80\x01\xc7\x93\x2f\xe6\x09\x85\xae\xaa\x75\xda\x3c\xa5\x1c\x23\xe1\x7f\x16\x59\xb2\xa2\x52\x52\x5b\x87\x1f\x58\x91\x0e\x7e\xa2\x24\x93\x53\xac\xb5\x41\x83\x65\x58\xd3\x64\x96\xb7\x98\x70\xbd\xaf\x3b\x0e\x3b\xf3\xfe\x43\x9d\x5a\x5c\xd0\xb1\xad\xef\xd9\x30\x09\x79\xea\x11\xa9\x4c\x3d\x4f\x72\xe3\xa5\x0c\x83\x41\xd7\xab\xb7\xb7\xe4\x30\x73\x55\x61\x24\xb5\xf3\x64\x4e\x73\xdd\x99\x18\x39\x18\xbc\x04\x67\x4a\xb0\x0e\x00\xf9\x36\x9c\x70\xe6\x07\x4d\xef\xec\x5d\x4a\x8b\x00\x38\x7f\x68\x14\x38\x33\xc2\x5c\x75\xe1\x86\x05\x93\x5d\x47\x5b\xe4\xe1\xd1\x89\xd5\x0c\x55\x04\x6e\x05\x38\x41\xee\x70\x29\x00\x9c\x04\x71\x55\xe3\x14\xf5\x28\xf7\x6e\xcc\x80\x66\x85\x74\xb0\xb1\x41\x19\xde\x82\xbe\x2b\x6f\x60\x9f\xa1\xdd\xc2\xbb\x2f\xf9\x18\x7e\xee\x09\xe1\x8e\x17\x0c\xc7\xf0\xf0\xe2\xe0\x2b\x35\xb7\xdf\x9f\xc1\x6b\xbe\x46\x4f\x46\x30\x9e\xa6\xed\x99\x14\x05\x2a\xea\x31\xd4\x31\xcc\x5b\x54\x33\x08\x73\x9c\xe9\x31\x02\xa3\xf8\xfe\x91\x71\x3b\x66\x00\xeb\x23\x04\x18\xfe\x15\x43\xd3\xd5\x8b\x8e\x81\xe9\x97\x9f\x73\x58\xfa\x79\xb7\x06\x65\xba\xc9\x32\xea\x3a\x20\x78\xeb\xc1\x07\xd7\x04\xe4\xa1\x37\x8a\x72\xbd\x6d\x74\xd2\x72\xce\x83\x0a\x00\x49\x77\xe4\x6d\x75\x02\x4e\x75\x38\xa9\x82\xd3\x4b\x7e\x88\x61\x1e\x1c\x85\xbc\x26\xd9\x06\x68\x82\x61\xe9\x60\x0f\x58\x35\xde\x72\x68\xc4\x29\x0a\x07\x31\x3f\xfc\x3d\xba\xbe\x27\xe4\xee\x77\x56\x8a\x74\xba\xaf\xa5\xe9\x92\x2e\x0e\x4e\x5f\x8b\x91\xf4\xf4\x38\x28\xf6\xb3\x85\xcd\x71\x01\x7d\x3d\x70\x6e\xf4\x35\x94\xb7\x55\xe1\x8d\x27\x6c\x9c\x42\x70\xd3\x1b\x45\xb9\x47\xa7\x0b\x5a\x6e\x01\x4b\x00\xbb\xeb\xfd\xad\x26\x87\xb9\x3a\x3e\xe4\xa4\xea\xe9\x7d\x1d\xc2\x37\xde\x0d\xc8\x6b\xc6\x3b\x68\x82\xae\x03\x5a\x0f\x58\x35\xde\x76\x3a\xc9\x29\x0c\x27\xb9\x31\x8c\x7b\x28\x00\x27\xe5\xee\x7f\x51\x6e\x4b\xd5\xdb\x62\xba\x4f\x53\x4f\xf7\x73\xa2\x9e\xde\xd7\x00\x7c\x9d\xaf\x03\x5e\xd3\xf7\x80\x7f\xbd\xef\x35\xd9\x7b\x45\x7c\x45\x20\xc3\xe9\x65\x60\xeb\x68\xe3\x1c\xf4\x62\xb8\xae\x58\x52\xf4\xdc\xe9\xdc\xde\xd6\x1f\x0d\x27\x8c\x1e\xae\x75\x9d\xe4\xe1\xc7\x94\x66\xf6\x31\xa5\xc4\x3e\xc4\xe3\x83\xd5\x5a\x35\x44\xe8\xb4\xe3\xc2\x3a\x0c\x94\x3d\x1e\x66\x09\x7d\x35\x25\xec\xfa\x32\x7a\xa7\x18\x65\x09\x7d\xd9\x19\xe4\x65\xc3\xdd\x5f\x4e\xa2\xcb\xbb\x82\x06\xe9\x53\x62\x1e\xe3\x5a\xfb\x0e\xe6\x02\xea\xd2\x93\x8f\x90\x8f\xe2\xd2\xeb\xf0\xd9\xfc\x7b\x58\x04\x58\x5f\xf5\x4c\x85\x68\xfd\x87\xaa\xea\xf4\x8b\xd5\x66\x97\x05\x9c\xb9\x33\x9e\xca\x31\x0e\xf8\xbb\xae\x76\x8f\xc4\x9b\xcc\xbe\x7c\x02\x83\x54\x8a\xe0\x49\xb4\x42\xa6\x9b\x7b\x32\xe3\x8a\xae\x03\xe0\x16\x45\xcb\x58\x07\x55\x61\x22\xc9\xe4\x7b\xf6\x1b\x7f\xc8\x18\x76\x33\xa3\xc5\x0b\x7d\xc1\xc4\x10\x26\x83\x89\xe9\xcc\x8f\xbf\xfc\x70\xed\x60\xd4\x98\x02\xe1\x4a\x4f\x28\x33\xa8\x7d\x61\xa4\xf4\xd6\x05\xc7\x45\xaf\xd7\x53\x69\x73\x3e\xf9\x14\xc5\x17\x11\x96\x87\x44\xed\xb9\xe9\x93\x3d\x68\x3d\x78\x0e\xc9\x75\xe2\x2a\x83\x28\xc6\xfe\x8c\x1b\xda\xd6\x55\xd9\xb2\x9b\x7c\xec\x8b\x7a\xe0\xd6\x3b\x9c\xd0\xaa\x22\x71\x33\xc5\xa8\xc3\xf1\xd9\xae\x5a\x02\x0a\x16\x82\xdf\xb6\x36\x78\x33\xa9\x6b\x79\x33\x78\x29\x3e\xb2\xac\x22\x6b\x9c\xbc\x75\x58\x5c\xc9\xda\x53\xd7\x4f\xc6\xfa\x97\x46\xe3\x5d\x4d\x06\x37\xd0\xee\xb5\xf7\x1a\xda\xd7\x71\xe6\x96\xea\x68\xcd\xff\xf0\x62\x8f\xba\xec\x6e\xbd\x30\x5a\xd5\xe9\xb7\xea\xf0\x7b\xb6\x6a\xb4\x2a\x7f\xab\xee\xda\x1b\x77\x95\xf7\x5d\x25\xfa\x16\x99\x8d\x0f\x92\xdf\x60\x10\x80\xf9\xfb\x57\x1e\x03\x77\xaa\x29\xa0\xc3\x7e\xab\x9a\xbc\x6d\xba\x67\x4f\xdc\x53\xd6\xf7\x94\xe6\x1b\xe4\x65\x2b\xff\xd5\x53\x00\xc8\x60\x25\xd8\xc1\x9c\x51\xb3\x04\xf3\x2d\xef\xb0\xde\xb9\x8a\x3b\xd1\x87\xf0\x43\x13\xaa\xb5\x7e\xba\xbd\xb7\x19\x4e\xf7\x0a\x96\x9c\xf2\x1c\xa9\xf3\xff\x02\x71\x5f\x33\x6b\xbe\xa9\x9a\x70\x3f\xe0\x0d\x9d\x7c\xbf\xd6\x8c\x54\x33\x32\xcb\xde\x4d\xfc\xf7\x93\xf0\xfd\x84\x78\xb3\x9c\xd0\xe1\xf0\x1b\xeb\xfa\x5d\x0c\xcd\x68\x47\xfc\xfa\xe6\xec\x6e\x6d\x19\xed\xc0\xbb\x98\xd0\xb1\xb9\xf4\x5e\xd2\xbd\x9b\x00\x6f\x95\x51\x14\x60\xf0\xd1\x60\x44\xdf\xea\x4f\x66\x90\xc3\x2a\x02\xfd\x6a\xcc\x4a\x9f\x00\xb0\x4f\x1d\x3e\x99\x71\x72\x77\x68\x14\x1b\x8a\x21\xde\x9c\xc1\x59\x88\xd3\x86\x85\xf4\x50\x4b\xc0\x21\xa8\x59\x87\x1d\x8d\x51\x90\xa0\x91\x63\x44\x39\xa7\x4f\x5d\xf3\x64\x98\xc0\x68\x14\x7e\x64\x7a\x1b\x30\x7a\x5d\xba\xa6\x06\x05\x1f\x5c\x43\xef\x8a\x5e\x53\x83\x82\x0f\x9e\xa2\x83\xe5\x76\x1b\x8d\x37\xf0\xe1\x90\xee\x6d\x34\xde\xc0\x87\xa3\x0f\x6e\xa3\x71\x72\x05\x61\xd5\x23\xd7\xa1\x3a\xed\xf4\x69\xf1\x6e\xf0\x4d\x30\xa8\xcc\x43\xe8\x03\xf1\x86\xd1\x07\xb2\x0c\xa1\x0f\xc4\x16\x36\x41\xde\x28\xb1\x40\x55\xbe\x95\x87\x20\xa9\x06\xaa\xf1\xad\x3c\x04\x49\x3e\x50\x85\x5d\xab\xa2\xe1\xa5\xe1\x10\x25\xd6\x67\x91\x71\x2d\x36\x16\xe9\x01\x6a\x16\x5c\x03\x0a\x8f\xd4\x10\xa0\x27\x66\x9d\x6f\xa7\x71\x25\x1f\x8e\xb6\xbf\x9d\xc6\xc9\xf6\x87\xc2\xfb\x1a\x7a\x5c\x01\x5d\x0d\xc1\xbd\x3d\x2d\x46\x46\x28\x7d\x0c\xfc\xc6\x7e\xf6\xd6\x78\x2d\x81\x2b\x79\x08\x6a\xf5\xb5\x04\x4e\x8e\xdd\xca\x91\x55\xdc\xc8\xcd\x0f\x93\x80\x7b\x9f\x1c\x5e\xb6\x81\xa0\xfc\x8f\xb1\x0b\x26\xe8\x3b\x1c\x26\x91\x4f\x36\x2d\x33\x01\x9b\x8d\xa4\x1d\x31\xf1\x0a\x07\x45\xfb\xe4\xd9\x46\x56\xa7\x2a\xc2\x10\x07\x6f\x1f\xf1\xb1\x3d\xfe\xbe\x4e\x1e\xdd\xc8\xf5\x37\x85\xa3\x98\x0c\x45\x8e\xe5\x87\x7d\xd2\xc6\xe6\x48\x4b\x8d\xed\x79\x67\x02\xc0\x3e\x21\xdd\x01\xae\xef\x84\x9e\x1b\xb9\xb2\x1a\x5f\x2f\x38\x93\x8f\x06\xb6\x00\xc9\x49\x0b\xd8\xf5\x3e\xa6\x88\xd2\xc5\xbb\xc9\xc5\xb1\x58\xd2\x3a\x99\xd6\x52\x86\xfb\x2e\x79\x68\xe0\xbe\x5e\x7a\xf3\x75\x12\x5f\x4d\xb7\x74\xd4\x15\xed\x78\x43\xfe\x60\x94\xee\x9b\xfa\x0a\xe3\x5b\x4b\x2d\xe3\x4b\x9f\xa8\x81\x7b\x07\xd5\x1d\x72\x35\xfa\x2a\xbb\x69\x68\x85\x37\x05\xe9\x31\xc0\xb4\xeb\xa0\xbf\x97\xf4\xdb\x06\x18\xc2\xfa\x90\x3a\xc5\x97\x2b\x72\x80\xf5\x76\xd7\x1d\xb2\x52\x3a\x6b\xba\xa5\xaf\x42\x1b\x81\x75\xd4\xc0\xae\xeb\x94\xbe\x9b\xee\x9b\x7a\x09\x63\x5a\xcb\x05\xe2\x4b\x89\xa9\x81\xfb\xfa\xea\x1e\xf9\x37\x7d\x95\xdd\xd2\x5d\x57\x34\x05\x9b\xb8\x06\xa6\x5d\x67\xea\xbd\xa4\xdf\xd4\x69\x18\xeb\x30\xd3\x85\x2f\xff\x27\x84\xf6\x75\xd9\x3d\x72\x8d\x7a\xea\xba\xa5\xc7\xc2\x1b\x82\x75\xd8\xc0\xb2\xeb\x20\xbc\x8f\xf2\x9b\xfa\x4b\x67\x9c\x9e\xf7\x34\x33\x57\x14\xa1\x57\xeb\xe5\x91\x78\xfd\x3d\x82\x04\xcb\xe6\xe9\xac\xcf\xfa\x22\x0f\xe3\x5a\x90\xec\x03\x5b\xab\x59\x45\x39\x4b\x71\x8a\x21\xf1\x1c\x12\x58\xc9\x73\x9e\xd1\x4a\x9e\x31\x54\x0d\x26\xfb\xb6\x2a\x2e\xdd\x90\x7c\x59\xac\x73\xe0\x6d\x80\x21\x65\x01\x48\x37\x8f\x65\xf8\xb4\x56\x61\x56\x5b\x67\xeb\xfd\xeb\x56\x4b\x88\xa0\xee\x2a\xac\xa7\xf3\xd5\xef\x9c\x88\xcb\xfd\xeb\x02\xc5\xdb\x0c\x48\x5f\xa9\xb8\xec\x79\xce\x4b\x2b\xab\xe8\x70\xf8\x7b\xeb\xcf\x02\x3e\xee\xd6\x6b\xab\x0d\xba\xe8\xff\xdd\x9e\x9f\x35\xec\x1e\xc3\x38\x86\x26\x04\xa6\xb1\x7f\xbb\x54\x1d\xfa\x2a\xbb\x7e\x7a\x5d\x7c\xb5\xf3\x9b\x42\x8a\x71\x61\xdc\x7a\x98\xeb\xf9\x14\x90\x77\x0b\x18\x5a\x7b\xd6\xd1\xb6\x28\x16\xd8\xdb\x1a\x12\x84\xc3\xe7\x2e\x1e\x03\x1e\x6d\x18\x7d\x57\x25\x49\x86\x97\xd3\xf4\xfb\x21\x89\x5a\x2e\x1c\xf2\xa2\xeb\x3b\x98\x14\xf5\x89\x7c\xa8\x6a\x92\xe6\xdd\x6b\xf4\x43\x34\x4f\x58\x97\x88\x0f\xbb\x68\x3a\xd7\x18\x56\xb7\xdf\xf9\x5f\xf6\x35\x20\x58\x77\xc0\xe3\x23\x3e\x56\x56\x26\x2b\x32\x9f\xc3\xfe\xd2\x75\x55\x09\x24\xa8\x52\x61\xd6\x35\x25\x0d\x29\x53\xf0\x82\xaf\x6e\xbd\x90\xea\x87\x71\xc0\x92\x1d\xa3\xc3\xfb\x5c\x65\xa4\x88\xd9\x7b\xd4\x58\x36\x1c\x0d\xcc\x30\xb4\x87\xfc\x85\x67\x7f\x18\x8c\x4e\x03\x8c\xab\xcb\x00\xa9\xd4\x01\xb3\x64\x95\x3c\x9a\xef\xd2\x60\x86\x18\x8e\x41\x59\x16\xb7\x69\x53\x15\x05\x6b\x7f\x57\x5d\xd2\x13\x43\xbc\x74\xbd\xee\x98\xcd\x9b\x1e\x48\x46\x23\xd1\xd4\x2c\x27\x45\x75\xd4\x84\x0b\x53\xf7\x6a\xdf\xd8\xfb\xff\xd3\x85\x78\x82\x01\x7f\xce\x41\xfe\x89\xc2\x42\x40\x0f\x45\xb3\x4e\x01\x5c\x90\x8e\xf6\xe3\x39\x9e\xaf\x7e\xc7\x13\xb3\x9e\xdb\x00\xa0\x6a\x1c\xc6\x0b\xa0\xcb\x2e\x2f\xc7\x24\x87\xd0\x49\x46\xd9\x4d\xc6\x78\x4d\xbc\x8c\x26\x1f\x11\x05\x06\x5a\xaa\xd4\xe4\x05\x2a\x91\xfa\xfa\xaa\xbd\x7d\x61\xb7\x0f\xf7\x26\xc4\xd4\xa9\x3f\xa5\x61\xe4\x58\x62\x94\xc4\xc3\xd5\x1e\x52\xee\x5b\x76\x48\xf2\xdd\xb4\xc8\xeb\x5d\x34\xcc\x9a\xd6\x24\x87\x96\xdb\xf3\xdc\x76\xbb\xc5\x4b\x8c\x89\x63\xfe\x11\x9f\x16\xf4\x21\xe6\xbe\xdb\xb7\xa8\x5f\xfa\x59\xc2\x24\x8b\x5d\xed\x73\x82\xea\x02\xed\x1b\x9b\x35\x55\x7d\x77\x0b\xb4\x4c\x1c\xbd\x91\x24\x09\xce\x02\x37\x26\xbf\x78\xac\xbb\x61\xdc\x5d\x74\xf2\xd2\x4b\xc5\x39\x47\x08\x3a\xbd\xab\x2e\x5f\x11\x02\xbe\xd2\x6c\x3d\x5d\x2e\x4c\x77\x69\xec\x66\xe3\x77\xec\x05\x40\xbc\x02\x30\x19\xc1\x10\x78\x3c\x37\x95\xde\xbc\x83\x88\x5f\x44\x95\x0f\xf2\x18\x42\x51\x37\x37\xd1\xd1\x82\xde\xe7\xe4\x98\xe8\xdd\x40\xd1\x5e\x98\x76\x5e\xf9\x20\x8e\x18\x38\x26\x01\x41\x7b\xba\xef\xca\x4f\xec\x91\x33\xd7\x9e\x82\xf6\xa8\x8e\x8b\xc5\xe1\xd1\x34\x9c\xe2\xf0\x60\xbb\x1b\x9d\x39\x85\x9f\x86\x9f\x36\xbe\xa1\x6d\x7c\x96\xdc\x93\x26\x3e\x53\xd2\x5e\x1a\x73\xb1\x64\xad\x1d\xe2\xed\x76\x2b\x3c\x3c\x61\xee\x56\xc2\xab\x96\x7d\xb8\xb2\x1e\x3f\xe0\x95\x88\x8a\xc5\xe3\x55\x1f\x22\xf5\x64\x55\xa4\xbd\x59\x65\xd9\x5a\x59\xcf\x3a\x91\x2f\x4c\x49\xfd\x61\xaf\x92\x71\x3b\xcd\x1f\x98\xb2\xad\xab\xc3\x02\xad\xc4\xe5\x50\xdc\x04\xbd\x0b\x05\xd6\xeb\x15\x8e\xb0\xe4\x77\x91\x24\xda\x0b\x56\x58\xc3\xb7\xdb\xb9\xd1\xf0\x42\x6f\xf4\xd6\x20\x32\xed\xaa\xaa\xe8\x72\xd3\xd0\xc1\x6e\x02\xd6\x6b\x93\xe0\x8b\x5c\xe6\x54\x1f\xc8\x39\x2f\x5e\x77\xd1\xfb\x7f\xa5\xc5\x33\xed\xf2\x94\x44\xff\x46\x2f\xf4\xfd\x24\x52\x1f\x26\xd1\xbf\x34\x39\x29\x26\x51\x4b\xca\x36\x6e\x69\x93\x1f\xcc\xeb\xc1\xd8\xf3\x87\x4b\xcc\x4d\x9f\x2e\x7d\x1e\xaf\xcb\x26\x8a\xe6\x8e\x19\xc3\xad\x69\x0c\xb7\x26\x81\xae\xaa\x75\x23\xb0\x92\x37\x5a\x35\xab\x05\x96\x29\x12\xd3\xca\xbb\x28\x74\xc2\x1a\xd8\x08\x2e\xc8\x60\xe8\xaf\x18\xc1\x35\x53\x36\xba\xaa\x45\x78\x8e\xf3\x12\x79\x41\x6e\x9e\x60\x4f\x5a\x3e\x5c\xf1\x22\xc7\x35\x79\xe8\xc4\x72\xc8\xbd\x9d\x2a\x79\x25\x4d\x53\x7d\xf5\xe8\x33\xf6\xde\x48\x62\x2f\x6e\xf1\xdb\xf7\x22\x6b\x23\x33\xe2\x98\x4a\x20\x5c\x20\x5e\xc1\x8a\xc7\x3e\x74\xb1\xeb\x93\xa6\x34\x86\xfc\xe1\x4a\xeb\xd2\x31\xe2\x36\x00\x36\x78\x7a\x4e\x84\x97\xe1\x49\x47\xcb\x5f\x31\xb3\x3d\xde\x93\x1d\xae\xf2\x81\xb2\xc1\x93\x4f\xde\x8b\x1d\x27\x2b\x7c\xa3\x9d\x77\x8c\x9d\x71\x93\x8f\x66\x2f\x13\x26\x23\xfc\x2c\xae\x87\x15\x57\x27\x41\x4e\xa0\x7b\x19\xc6\x4a\x22\x99\x79\xd4\x0f\xef\xf9\x38\x11\x66\xc5\xc5\xcb\x4d\x9a\x8b\xf1\x61\x84\x59\x9d\x9c\x38\xf5\xd7\xf2\xbb\x75\x6d\xf1\x88\xe6\x4d\xec\xf8\x95\xc6\xa9\xbc\x77\x63\xa7\xae\x6a\x15\x1e\x1a\x8b\xc3\xe2\xeb\x8e\x35\x1e\xf9\x80\xd6\x7c\xb3\x36\xdd\x78\x30\x15\xdf\x67\x72\x5f\x86\x4f\xee\xe0\x1d\x4d\x38\x77\xa8\x47\xa3\xf4\x37\x8d\x9b\x33\x29\xfe\x6e\xcb\xdc\x34\x4d\xdf\xbc\xcc\xf5\xb8\x95\x89\xed\x29\xce\x91\x95\xad\x1b\xd6\xd0\x22\xe5\xba\x68\x5a\x0a\x83\x0a\x12\x70\xf0\x54\xb4\xf1\x8e\x81\x02\xc7\x44\x4b\x9d\x8d\x80\x2a\x3f\xc4\x58\x87\xd8\xa0\x60\x89\xa7\x94\xf2\xa1\x6f\xa6\xf6\x24\x2b\x92\x63\x66\xe9\x8e\xcb\x6f\xfa\x7f\x23\x8b\xd3\x7d\xff\x0f\xe9\x2b\x65\xe5\x23\x73\x64\xea\x11\x18\x10\xb9\x66\xbc\x18\xc0\x4f\x53\x66\x40\x26\x91\xf1\x61\x47\x0e\x9d\x77\x90\xdb\x8e\xf7\xdd\x7d\x19\x9d\xa3\x08\x49\xdc\x32\x73\xb5\x07\xb0\x2f\xa4\xb1\x8b\xde\xbf\xb7\x4d\x1f\xa6\x13\x5d\x55\xeb\x55\xca\x94\xd3\xc2\x06\x79\x26\x1f\x09\x82\x4c\xff\x5a\xfc\x09\x96\x98\x43\x64\xf5\xd1\x36\xc3\x56\xe6\x10\x8b\x55\xd0\x60\xa0\x45\x08\x87\xea\x91\x3a\x29\x95\xe8\xbd\x83\x63\x2d\xf3\xd5\x18\x27\x6c\x78\x42\xb1\xd9\x2e\x8c\x92\x8e\x3e\xd6\x75\x91\xe9\x8e\x8a\x2e\x34\xad\xcc\x2b\x36\xe6\x63\x84\xb0\x8a\x88\x4d\x89\x48\x18\x18\xaf\xbc\x74\x76\x75\x89\x79\x99\xe0\xb5\x59\x02\xbb\x51\xc9\xe0\xd0\xc3\x27\x70\x5d\x92\x7a\xa1\x2d\x4a\x2f\xb7\x40\x66\x32\x90\x74\x93\x9e\x8d\x33\x0d\xcf\x85\x01\x83\xed\x52\x33\xf9\xde\x6e\xb8\x9e\x21\x2c\x68\xee\xa9\x2e\x35\x58\x34\x2e\x33\xc0\x29\x90\x98\xcc\x8a\xa3\x2f\x7a\xc6\xc4\x15\xc6\x2d\x10\x57\x4a\x9a\xea\xd2\x52\x73\xd7\x4a\xc6\x15\x4d\x30\xb0\xa6\xf6\x46\xff\xe5\x46\xb9\x6b\xa7\x4c\xa7\xf7\x34\x0d\x4d\xfe\x2f\x5d\x40\x6c\x53\x4a\xbe\x24\x29\x5e\x01\x57\x5e\x97\xb1\x15\xe5\x02\x1b\x83\xf1\xb1\xfe\x24\x1f\x18\x47\x0b\xe5\x5b\xe3\xba\xb7\xa8\x07\xc4\x48\x51\xf0\x37\xec\xd5\x5e\x4e\xbc\xc8\x3e\x46\x93\xe8\x83\xbd\xd9\x16\x2f\xb2\x48\x84\xcb\x9c\x72\x0c\xdd\xb6\x5b\x5b\xef\xab\xfb\xb7\xee\x10\x78\xc7\xf6\x1d\x46\x19\xb8\xb3\x07\x92\xd2\xf8\x39\x6f\xf3\x7d\x5e\xb0\x68\xd5\xb0\x01\xf5\x6e\xac\x5c\xd2\xa9\x69\xd3\xd6\x94\x67\xa4\x64\x2f\x8d\xb0\x52\xeb\xab\x7a\xe2\x1f\x11\x96\x48\x68\x39\x95\x6f\x8d\xa0\x30\xa5\xcc\x21\x09\xd7\x29\xde\xed\xbd\x45\xf6\xa1\x1f\x02\x7c\xd8\x7f\x1c\xc4\xe4\x05\x0b\xe1\x53\xbc\x45\x82\x82\xd4\x0d\x7d\xbe\x9a\xcd\x38\x90\xcf\xf8\x3a\x46\x7d\xa2\xe4\x8d\xe8\xeb\x72\xb7\x03\xb8\xf1\xd7\xb4\x26\x09\x68\x49\xa2\xb5\x02\x1f\xd4\xce\x46\x94\x3c\x41\xba\xf9\x59\x89\x1e\x7f\xcf\x06\x27\xcf\x8d\x82\xbe\x21\x71\x6d\x7d\x63\x0b\x6b\x60\x8f\x7d\x55\x00\x56\x7c\xa0\xaa\x5a\x35\x79\xfb\xc9\xba\xd4\xd5\xe8\xe1\x11\x19\x00\xd5\x0f\xad\x1c\x8e\xea\xb1\xb6\xf5\x53\x69\x53\x99\x33\xe0\x6d\xe7\xc5\x56\xe6\x03\xd0\x57\xbd\xf5\x8c\x9c\xd8\x41\x1e\xa9\x5e\x33\xed\xbe\x7e\x47\xd4\x6c\xf0\x20\xd0\xf0\x47\xcb\x78\x7f\x5a\x5b\x40\x51\x32\x3c\x66\x05\x0e\x6b\x25\xc9\xec\x23\x93\x79\xf8\x33\x62\x77\xae\x40\x34\x63\x20\xcf\xaa\x9b\xb0\x09\x3d\xea\xaa\x7a\xc2\xb7\x3c\xf9\xcf\x43\x53\x9d\x3f\x58\x55\x7f\xe4\x4f\x7d\x55\x66\x09\xab\xfc\x63\xe0\x8b\x62\x5d\x15\x89\x19\xe6\xb6\xa6\xc9\xde\xae\x9b\xea\x98\x67\xbb\xff\xf2\x3f\xff\xd8\xd7\xf3\x67\x69\xe0\xa6\x7f\xca\xd3\xa6\x6a\xab\x43\x37\x55\x55\xb6\x1d\x69\xba\x3f\xf4\x6a\xd7\x76\xcd\x0f\xdf\x7f\xf7\x90\xf0\xff\xfb\x9e\x55\x47\xcb\x0c\x94\x25\xaa\x2c\xfa\x6f\x02\xff\xcf\xaf\x35\xfd\x61\x66\x36\xaf\xa1\x35\x65\x87\xd5\xd8\xff\xc6\x2f\x4e\xdd\x1a\x46\x1e\x0c\x06\xf3\x11\x23\x8f\x86\xbc\x51\xed\xb8\x88\x50\xe9\xad\xee\xa2\x76\xb7\x57\xf0\x66\xb5\xe3\xca\xe5\xd0\xbc\xd5\xdb\xd5\x2e\xb0\x69\x77\x50\xbb\xc4\xa3\x76\x0f\xf7\x56\xbb\xe1\x78\xa2\x59\xe0\x4c\x58\xee\xdc\xcd\x1b\xdb\x65\xd5\x8e\xf8\x20\x7b\xae\xd6\xfc\x32\x3d\x16\xaf\xf5\x29\x4f\xab\x32\x4e\x4f\xf4\xb9\xa9\xca\xd8\x9c\x1e\x3d\xa0\xbc\x1f\x35\xaf\x4b\x41\x33\x40\xd3\x53\xd0\x4b\x43\x1c\x06\xb1\x3a\x56\x01\xf8\x95\xb6\xf4\xca\x4b\xb6\x7e\xc1\xfd\x9a\x5b\xda\xa8\xf8\x12\x67\xb5\x7d\x91\x0c\x10\x7d\x0b\x17\x96\xb3\xd2\x5e\x54\xa2\x52\xb9\x23\xa3\xd5\xaa\xe2\x03\xde\x6a\xaf\x91\xba\xda\x7c\xd6\x4f\x89\xc8\xbf\xad\xa0\xb6\xb5\x85\xa1\x76\x26\xd0\x45\xa4\xaf\xfe\xdd\x9e\x1e\xaa\x86\xea\x21\xce\xef\xff\x32\x4f\x16\xdb\xef\x03\x1a\xe7\x46\x27\x36\x7a\x5e\x66\x79\x4a\xba\xaa\x69\x3d\xba\xa6\xc2\x8e\x09\x12\xc1\x1a\x76\x7f\x56\xc0\xaf\x5a\xf3\x42\xf7\xdb\x82\xf2\x10\x00\x87\xc3\xbd\x2b\xd7\x03\x86\x18\xf7\x45\xae\x7b\xf3\x50\xf9\x35\xb7\x5a\xef\xcf\x59\xa2\xc7\xf8\x67\xe0\x88\x55\xdf\xae\x5e\x76\xb1\x3a\x3a\xe4\x3d\x8c\x0c\x77\xd4\xa2\xbf\x6c\xf1\x42\xdd\x74\x27\x1f\x1d\x7b\x3d\x7a\xb8\x51\x6e\x0c\xa0\xda\x0d\x64\x00\x57\x29\xb2\xbd\x73\xa3\xbd\x73\x64\x4f\xc3\x7f\x6f\x73\x50\x35\x9e\x0c\xda\xa3\x28\x2a\x31\xf4\xef\xa0\xda\xcc\xf5\xa8\xeb\xca\x50\x1b\x78\x65\x85\x0f\x28\xe3\x62\x84\x75\x11\xe2\xae\xae\xb9\xb3\x99\xea\x20\x9b\x46\x09\xe8\xa1\x08\x04\xb5\x69\x43\x69\xc9\x63\x41\xea\x8c\x94\x76\x36\xec\xd7\x9a\x58\xde\x3a\xb3\x0c\x47\xbe\xc4\x09\x35\xa9\x25\x0b\xfd\xc8\x9a\x34\x73\x62\x1f\x19\xae\x96\x16\xea\x9c\xd7\x1b\x9b\x09\xe7\x16\x66\xe7\xcd\x09\x65\x75\x5d\x45\x61\x13\x8a\x56\x93\x9a\x44\xd0\xaa\x80\xf2\x4b\x3d\x9f\x73\xe3\x25\x14\x5b\xfe\x65\x6a\x2d\x2a\x22\xc3\xee\xbe\xd3\xb3\x3f\x0c\x61\x0e\x75\xb8\x32\x96\x4f\xc9\x3e\x0d\xdf\x78\xf0\x79\x12\x8d\x40\xf1\xf9\x80\x79\x22\xfd\xc7\xae\xaa\x8a\x3d\x69\x34\x64\xf9\x4d\x80\x46\xd3\xb4\xa0\xa4\x39\xe4\x2f\x0a\x4a\x7d\x00\xd4\x7a\x91\x92\xbc\xa4\x4d\x7c\x28\x2e\x79\x36\xc0\x1a\xdf\x07\xaa\xb2\x40\x80\x6a\x44\x06\xb0\xac\x88\x4f\x55\x93\xff\xdc\x97\x14\x51\x36\x10\xb6\x0a\x00\x33\x2c\xce\x0a\x4a\xf9\x07\x5d\x4e\x3e\x18\x40\x0a\x1e\x50\x55\xb8\xda\x47\xc5\x6a\x49\x9e\x15\x44\xff\x1b\x50\x29\xc9\xf3\x9e\x34\xea\x4e\x20\x04\xd3\xbe\x43\x5a\x7d\x01\x3f\xa0\x0c\x24\xa4\x7f\x37\xc0\x0d\xb2\x43\x71\x4d\x8e\x1a\x15\xfe\x37\x28\x96\x17\x14\x15\x05\xf0\x49\x81\xa9\x2d\x0e\xf1\x9b\x17\x08\x57\x4c\x7f\x4f\xd9\xdc\xe4\xb8\x5e\x85\x4d\xcd\x34\x94\x30\x48\xe5\xdc\xba\x32\x10\x09\x51\x15\x54\x07\x86\xde\x0e\xe8\x60\xad\x1f\xad\xee\xb2\xbb\xc7\xea\x0f\x63\xb3\xbb\x17\x06\x7b\x41\xfa\xa4\xa6\x2c\x36\xd9\x81\x23\xd3\xf6\xce\xbd\x6e\xda\xf4\x2b\x1e\xf1\xb0\xb8\x57\x3b\x5c\xfa\x8b\xdd\xf0\xa2\x5b\xf4\x4f\xf9\xb9\xae\x9a\x8e\x94\x9d\x06\xad\x62\x52\x02\x98\xfd\x6d\xc3\x9e\x72\x71\xbd\x40\xdb\x19\x42\x00\xdb\x93\xd8\xff\xd3\x1b\x83\x40\xe6\x25\xdb\x6d\x90\x2f\x73\x5b\x3b\x0f\xea\x94\x55\x3f\x7f\xab\xfa\xfb\xc9\x6b\x17\x25\x9f\x93\x88\x00\x5f\xc2\x38\xba\x60\x4d\xf9\xb8\x9b\x84\x1e\x78\x00\x71\x51\xce\x47\x50\xb3\xc9\xe1\x90\xbf\x60\xf7\x40\xa4\xb3\xf1\xed\x37\xf1\xb9\x8d\x9f\x73\xfa\xb5\xc7\x84\x3e\x5e\x46\x9f\xf3\x94\x72\xbf\x43\x92\x13\x92\x89\x8b\xe3\x24\x52\x7f\x9c\x33\xf0\x47\x7b\x06\x7f\xbc\xb4\x41\x4c\x0e\x54\xb9\xd2\x01\x0a\xc5\x31\xe6\x2e\x37\xf6\x4d\x40\xf7\x7a\x3e\xf0\x62\x91\x38\x67\x36\x09\xf5\x0d\x21\xd1\x9e\x2d\x12\xed\xd9\x26\xa1\xbe\x21\x24\x5e\x5a\x8b\xc4\x4b\x6b\x93\x50\xdf\xb0\xb1\x86\xcb\x6a\x38\x36\x2f\xcf\xbe\x45\xbb\xcd\x7a\xa3\x7c\x42\x43\xf2\x3e\x6d\x67\xae\x03\x33\xb1\x6e\x2c\x56\x2c\x41\x9b\x11\xb8\xb8\xa9\xbe\x22\x35\x64\x00\x6d\x12\x75\xa7\x31\x2a\x29\x2d\x0a\x8b\xcc\x95\x8d\x07\x02\x1d\x13\xc1\xb5\x94\x79\x8f\x19\xa4\xc5\xc7\xfb\xd0\x46\x99\xd7\x8a\x46\xea\x31\xaf\x92\xf0\x35\x84\x3a\x2c\xb9\xdd\xce\xac\xca\xe5\x6d\x8d\xeb\xf4\xc5\xc2\x72\xe8\x0b\x0e\x37\xaa\x2f\xbd\x2d\x01\xfa\xe2\xa0\x12\xa2\x2f\x37\x49\xe4\x76\x25\xba\xad\xba\x37\x68\xd6\x5b\x2a\xbc\xa3\xba\xf1\x0b\x3c\x46\xe5\xb3\xd9\x76\x6b\xd5\x7e\xce\x6e\xd1\x37\x0b\xcb\xa1\x6f\x38\xdc\xa8\xbe\xf5\x13\x19\xd0\x37\x07\x95\xeb\xf4\xed\x1a\x91\xdc\x43\xe1\xae\xaa\xef\x2e\x1a\x77\x43\x8d\x77\x54\xb9\x19\xbb\xc7\x63\x54\x24\xaf\x8d\x5d\xa7\x5d\x16\x96\x43\xbb\x70\xb8\x51\xed\xea\x7d\x26\xa0\x5d\x0e\x2a\xd7\x69\x97\xa3\xf5\xf7\x50\x24\x17\xe9\xbb\xe8\x8c\x9f\xf8\x9b\xd5\x03\x9f\x68\xb9\xdb\x6c\xbb\x1d\x96\xa7\xf5\x36\x03\x2b\xaa\xb1\x66\xab\xeb\xaa\x19\x1f\x55\xa2\x1e\xcb\x4a\x5d\x57\x8f\xd6\x13\x82\xa4\xa5\x9a\x6e\x92\xaa\xf3\xea\x26\x17\x07\xc8\xc3\x3c\xd8\x01\x1e\x21\x71\xe5\xc0\xc5\x10\x1d\x63\xd7\x09\x3a\x3a\x7c\x19\xa6\x36\x82\xdd\xb4\xdc\x83\x58\x47\xbe\xda\xef\x77\x49\xed\xda\x01\x6f\x60\x83\x31\xfd\x46\x3e\xae\xb6\x0e\x28\xfe\xfd\xe4\x72\x0f\x83\x02\x88\x8b\x01\x82\xf5\x3c\x3e\x46\xfe\x4f\x00\x00\x00\xff\xff\x47\x37\xb0\x07\xc9\x28\x02\x00") - -func cssBootstrapMinCssBytes() ([]byte, error) { - return bindataRead( - _cssBootstrapMinCss, - "css/bootstrap.min.css", - ) -} - -func cssBootstrapMinCss() (*asset, error) { - bytes, err := cssBootstrapMinCssBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "css/bootstrap.min.css", size: 141513, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _faviconIco = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5a\x0b\x70\x55\xc7\x79\xde\x7b\xce\x45\x12\xb2\x90\xc4\xc3\xe6\x61\x3b\x90\xf8\x31\xc4\x19\x6c\x32\xe3\xc4\x34\xe3\xc6\x34\x6d\xed\xd4\x69\x62\x32\x49\x9a\xa6\x75\xea\x36\x33\xee\xd8\x9e\xb4\x75\xdc\x7a\xa6\xc5\x31\x02\xa6\xd3\x84\x47\x28\x6f\x3b\xc6\x3c\xcc\xeb\x9e\xbd\x08\x21\x04\x92\x28\x12\x0e\x08\x5b\x3c\xec\x02\x92\x78\x08\x21\x09\x41\x25\x24\x10\xe8\x71\xb5\xe7\xdc\xd7\xb9\xf7\xef\xfc\xff\x9e\x73\x74\x25\xdd\x97\x24\x82\x33\x1e\xce\xcc\x3f\x7b\xee\x9e\xdd\xff\xff\xf6\xdf\x7f\xff\xfd\xf7\xdf\xcb\x98\x8b\xa9\x2c\x3f\x1f\xcb\x19\xec\x15\x37\x63\x5f\x67\x8c\xcd\x98\x21\x7f\x6b\xf9\x8c\x6d\x72\x33\x36\x7b\xb6\xf5\xfb\x11\xc6\x9e\xbd\x97\xb1\x99\x8c\xb1\x7c\x6c\xc7\x64\x3d\x3d\x6e\x76\xdb\x1f\xe1\x75\xab\x42\x53\x7e\x22\x3c\x6c\x91\xf0\xb0\x02\x22\xae\x14\x08\x8f\xab\x40\xec\x64\x92\xf0\x5d\x53\xfe\x59\xec\x64\x5f\x15\x1e\xa6\x08\x4f\x7f\x7f\xdf\x7a\x96\xa1\x97\x3c\xb8\x3f\x78\xfa\x0d\x08\x9e\x5d\x08\xc1\xda\x5f\x82\x51\xf6\x65\x30\xca\x67\x41\xf0\xcc\xbf\x11\x19\x07\x1e\x07\xbd\x78\x2a\x18\x65\x8f\xb5\x0b\x4d\x7d\xad\x6f\x07\x73\x0b\xcd\x45\xfd\xbb\x17\xb3\x0c\xa3\x62\xce\xfe\xa8\xd1\x0a\xf8\x98\x6d\x25\x10\x38\xfe\x53\x88\xdc\x3a\x01\x66\x6b\x11\x11\xbe\x07\x8e\xfd\x2d\x84\x2e\x2c\x05\xff\xd1\x79\x3d\xc2\xc3\x7e\x20\x78\x06\x13\x5c\x89\xe9\xdf\x06\xd1\x50\x2f\x04\x4e\xfc\x0c\xcc\x6b\xa5\x60\x1c\xfd\x21\x74\x6c\x1e\x47\x84\xef\x58\x17\xf8\xe4\x65\x30\x6f\x54\x81\x71\xe0\x89\xea\xbe\x2d\x6c\xd2\x80\xfe\xfe\x76\x30\x6f\x1c\x81\x60\xed\x7c\x08\x9e\x5d\x0c\x2d\xeb\xc7\x41\xed\xd2\x29\x50\xbb\x74\x32\xbd\x63\x1d\x7e\xc3\x36\xa1\x73\xff\x19\x14\x1e\x36\x4f\x78\xc7\xc4\xf4\xef\x80\x70\xd3\xfb\x10\x6e\x5c\x0f\xdd\x65\x73\xe1\xdc\xf2\x89\xd0\xb1\x25\x0f\x6e\x7c\x90\x07\xe7\x7f\x33\x81\xea\xf0\x5b\xb8\x79\x13\x61\xd0\x8b\xa7\x2c\x09\x7c\xfc\x57\xac\x7b\x11\xf6\x7f\x6a\x5f\xd4\xb8\x06\xa1\xfa\x15\x10\x6e\x7a\x0f\x3a\xb4\xc7\xa0\x61\x55\x3e\x04\x8f\xfc\x39\x84\x3e\xfa\x4b\x68\x5c\x93\x4f\x75\xf8\x2d\x74\x71\x05\x44\x45\x13\xea\x54\xeb\x7c\x85\xa9\x48\xf8\x1e\x15\xcd\xf4\xcd\xee\x7f\x71\x65\x3e\x04\x3e\x7c\x86\x78\x5c\x5a\x1d\xd3\xbf\x7e\x05\xa0\x2c\x94\x89\xb2\x03\xd5\x3f\x66\x88\x05\x31\x21\x36\x1b\xff\xd9\xe5\x13\xe1\xda\xc6\x5c\x68\xdf\x94\x4b\x63\x71\xf0\x37\xbd\x0f\x38\x56\x1c\x33\x8e\x1d\x75\x80\xba\x40\x9d\x0c\xd6\x5f\xcd\x92\x29\x44\x83\xf5\x87\xba\x76\xfa\x73\x85\xe1\x5c\x18\x07\x9e\x38\x86\x18\x68\x8e\xac\xf9\xbb\xbe\x39\x87\xc8\x99\xbf\x13\x3f\xa3\x39\xc6\xb9\x8e\xed\x4f\xb6\xe0\x61\x3f\x40\xdb\x40\x1b\x41\x5b\x19\x62\x3f\xc7\x7f\x4a\xb6\x85\x0f\xda\x9a\xdd\x9f\xd6\x80\xe6\x62\xd2\x26\xd5\xd7\xd0\x46\xc9\x56\x0f\x3c\xde\x6f\xbf\xe5\xb3\xc8\xa6\xd1\xb6\xc9\xc6\x4f\xbf\x01\x68\xf3\x68\xfb\xce\x3a\xf2\x30\x26\x76\x32\x85\xd6\x08\xae\x95\xc1\xeb\x87\xd6\x14\xb3\x69\x11\xad\x39\xaf\x5b\x1d\xed\xfa\x05\x60\x8c\x65\x32\xc6\x54\x8b\x5c\x31\x64\x3d\x0b\x63\xe8\xb0\x45\x2d\x56\xdf\x99\x96\x8f\x99\x1b\xeb\x67\xf2\x47\x8b\xea\xf3\xf9\x08\xae\x22\xe5\x09\xae\x4e\x17\x5c\xfd\xc2\x28\x69\x8a\xe0\xca\x58\xb4\x3d\xdb\x17\xa6\x29\xff\x5f\x04\x57\x5b\x85\xe6\xba\x92\x90\xb8\x1a\x43\x4a\x4c\xbd\x62\xd7\xb7\x08\xae\xd6\x0b\xae\xfe\x8f\xe0\xea\x9b\x38\x1e\x1f\x1f\x4b\xfe\x34\xb5\x7c\xa5\x40\xec\xca\x02\xbd\x64\x3a\xe8\xfb\xbe\x38\x94\x4a\xa6\x83\xf0\x66\x82\xd0\x14\x10\x9a\x0b\x44\xe1\x38\x5c\x2b\x44\xf8\x4e\x75\x5c\x01\xe1\x75\x03\xf2\x11\x9a\x2b\x2a\xb8\x52\x2b\xb8\x3a\x4f\x68\x8a\x92\x0c\x03\xc9\xf7\xb0\x02\xa3\xfc\x2b\x10\xe9\xae\x85\xa8\x7e\x15\xa2\xfa\x95\x18\xba\x0a\xa1\xb3\x8b\x49\xbe\xbe\xe7\x5e\x08\xfe\xef\x3f\x81\xd9\x51\x09\x91\xbe\x4b\x44\xf8\x8e\x75\xf8\x4d\x70\x37\x04\x3e\xfe\x11\xf9\x14\xbd\x68\x3c\xe2\xe8\x14\x5c\x7d\x49\x78\x99\x2b\x11\x06\x47\xfe\x81\xd9\x10\x0d\xde\x82\xc1\x8f\x79\xe3\x30\xe8\x7b\xa7\x81\x51\xfa\x28\x98\xd7\xca\x00\xa2\xa6\xfc\x80\x65\xcc\x3b\x7e\xc3\x36\xfa\xde\x07\x08\x93\xd9\x5a\x0c\xfa\xfe\x87\x41\x68\xac\x43\x70\xf5\x59\x92\xe3\x1d\xea\x1a\x92\xc9\x8f\x06\x3a\xc1\xff\xe1\x5c\xda\x67\x91\x27\x3e\x91\xde\x0b\xe4\xaf\xfc\x47\x5f\x20\xc2\x77\xac\x23\xac\x1d\x95\xd4\xd6\xff\xe1\x9f\x10\x2f\xfa\x5d\xf2\x05\xc4\x70\x4c\x70\xf5\x7e\x94\x35\x1c\xf9\xa1\xc6\x77\x68\x3e\x43\x17\x57\x4a\xfe\xe8\xc3\xcb\xbe\x8c\xfc\xa0\xcf\x23\x09\xdf\xb1\x0e\xbf\x51\x9f\x8b\x2b\x65\x9f\xc6\x77\xe8\x37\xee\x4f\x62\xd7\x58\xb4\x8f\x05\x3e\x9e\x35\xc4\x1e\x13\xc9\x8f\x06\xbb\xc0\xa8\xfc\x23\xf0\x1f\xfa\x63\x88\x86\xba\x21\x72\xeb\x24\x18\xfb\x1f\x82\xde\x1d\x2e\x68\xdd\x30\x0e\x1a\x56\x4f\x24\xc2\x77\xac\xc3\x6f\xd8\x06\xdb\x62\x1f\xec\x8b\xef\x10\xd6\xc1\xff\xd1\xf7\x11\x67\x83\xe0\xea\x97\x06\xeb\x20\x91\x7c\xd2\xdd\xee\x3c\x08\x37\x6f\x04\x30\x0d\x08\x7c\x34\x8f\xe4\x34\xae\x99\x00\xa7\x7f\x3d\x0d\x4e\xfd\xea\x7e\x22\x7c\xc7\x3a\xfc\x86\x6d\xb0\x6d\xb8\x69\x23\xf5\xb5\xe7\xcc\x6c\x3f\x00\x62\x77\x6e\x54\x68\xae\x57\xad\xf5\x96\x52\x7e\xb0\x6e\x01\xe8\xfb\x66\xd0\x1a\x30\xaf\x95\x83\x28\xcc\x81\x2b\xef\xe6\x92\x3c\x24\x8a\x63\x96\x4d\x71\x7e\xe3\x37\x6c\x83\x6d\xb1\x0f\xf6\x0d\xd6\x15\x48\x5d\x86\x7c\x64\x47\xc2\xc3\xf6\x0a\xae\x66\xc6\xea\x20\xae\xfc\x48\x08\xfc\x55\xdf\x05\x7f\xd5\x77\x00\x22\x41\x08\x7e\xfa\x2a\x74\x6d\x53\xa1\x6e\xf9\x64\x92\x75\x69\xcd\x44\xb8\xb9\x35\x13\x6e\x6d\xcb\x80\xe6\x75\xe3\xa9\x0e\xbf\x61\x1b\x6c\x8b\x7d\xb0\xaf\xbf\xea\x7b\xc4\x8b\xec\xe2\xec\x22\x9c\x83\x66\xc1\xd5\x19\xa9\xe4\xd3\xdc\x1f\x98\x0d\xc1\x33\x6f\x42\x34\xec\x03\x7f\xe5\x1c\x68\xdb\x30\x96\xe4\x9c\xfb\xcd\x7d\xd0\xbd\x63\x0c\xe8\x85\x63\x41\x2f\xbc\x07\x7a\x3d\x6e\xa8\x5f\x39\x89\xbe\x61\x1b\x6c\x8b\x7d\xb0\xaf\xe4\xd9\x65\xd9\x6e\x19\x88\x5d\xd9\xba\xd0\x5c\x73\x53\xca\xd7\xff\x0f\xf4\xfd\x0f\x41\xa8\x61\x35\xf9\x1f\x7d\xdf\x74\x68\x5e\x97\x47\xf3\xdd\xf2\x4e\x1e\xc9\x0d\x9f\xfc\x3b\x30\x4f\xbd\x02\xc6\x9e\x09\xd0\xba\x21\x07\x4e\xfd\x6a\x1a\xb5\xc1\xb6\xe4\xb3\x1a\x56\x13\x0f\xe4\x25\xd7\xed\x39\x5c\x9b\x51\xa1\xb1\x17\xe3\xc8\x7f\x0b\x63\x13\x5c\xef\xd4\xb6\xaf\x91\x7c\x6b\xf8\xf2\x16\x5a\xdb\x62\xcf\x64\xb8\xb4\x7a\x3c\xc9\x68\xdb\x90\x0d\xfe\xb2\x87\x01\x2e\x2d\x07\x68\x5a\x05\x81\xca\x27\xe1\xfa\xe6\x4c\x39\x2f\xab\xc7\x53\xdb\x88\xaf\x9e\xfa\x22\x0f\xe4\xe5\xc4\x47\xa5\x8f\xa2\x0d\xfc\x22\x8e\xfc\x5f\xe0\x37\x3b\x5e\x4f\x2e\xff\x1e\xf0\x97\x7e\x11\xa2\x0d\x4b\x00\x9a\x56\x42\xe0\xe0\x13\x70\x7d\x53\x56\x6a\xf9\x81\x4e\x8a\xbf\x70\xac\x43\xe4\x6b\xec\x45\xd4\x0d\xea\x28\xbe\xfe\x67\x38\xfa\x6f\x5e\x97\x2f\xfd\xcb\xd1\xe7\x21\x74\xec\x87\xa0\xef\xce\x81\xab\xbf\x1d\x67\x7d\xcb\xb3\xd6\xcc\x50\xfd\xe3\xdc\xe2\x1c\xcb\xb3\xd0\x60\xf9\xae\xb9\x68\x1b\xe4\xdf\x53\xd8\x1f\xda\x39\xda\xbe\xee\x55\x41\xf7\xba\xc9\x16\xcf\xaf\xb8\x2f\xa5\xfd\x25\x95\x8f\x6b\x42\x63\xcd\xb8\xcf\x25\x5c\x7f\x5b\xe5\xfa\xc3\x39\xb8\xf0\xdf\xf7\x42\xfb\xc6\x7b\xa0\x63\x53\x36\x5c\x5c\x35\x29\xad\xf5\x97\x42\x7e\x26\xfa\x06\xda\x37\xc2\xbe\xb4\xfc\x0f\xd2\x99\x25\xd3\xd2\xf2\x3f\xc9\xe5\x2b\xf6\x1c\xbc\x86\x3e\x12\x7d\xe5\x50\xff\xeb\x8f\xe3\x7f\x25\x0d\xf5\xbf\xfe\x21\xfe\x37\x99\xfc\x18\x1d\x7c\x09\xf7\x08\xdc\x2b\xc0\xd4\x69\xef\x30\x2a\x46\xb9\xff\x58\x73\x9f\x5a\xbe\xc2\x7c\x1a\xed\x8d\x0b\x70\xaf\xc4\x3d\x53\xee\xbf\xef\x8e\x7a\xff\x4d\x47\x7e\x8c\x0e\xee\xc7\x58\x01\x63\x06\xd4\x1d\xf6\x41\x9b\x18\x51\xfc\x61\xf9\xb2\xb4\xe5\x7b\x55\x1b\xc3\xb3\x18\x33\x61\xec\x84\x31\x14\xf1\xdc\xfb\x40\x5a\xf1\x97\x4e\xf1\xd7\x34\x8a\xd9\x06\x3f\xa9\xe4\x3b\xb6\x48\xb1\xa2\xfa\x12\xc6\x8e\x18\x43\x62\x2c\x89\x31\x25\xc6\x96\x29\xe3\x4f\x6f\x26\xc5\xaa\xf1\x62\x58\x8c\x6d\x31\xc6\x4d\x26\xbf\x7f\x3d\x60\xcc\xac\xce\xa3\x18\x1a\x63\x69\x8c\xa9\xb9\x5b\xc6\xd8\x14\x7f\xe7\xc4\xc4\xdf\x39\xb2\x0e\x63\x73\x8c\x91\x93\xc5\xf0\xc4\x47\x49\x2a\x9f\x30\x68\x0a\xf3\x15\x65\x31\x6b\xaf\x7e\xd3\x3a\x53\xd4\x5b\x67\x8c\x44\xe7\x0f\x49\xc9\xcf\x30\xad\xd6\x59\x27\xa9\xfc\x58\x5d\xf4\x69\x14\x2f\x8d\x95\x67\xab\x51\x9f\xcf\xa6\x5b\x67\xbd\xb4\xe4\x7f\xde\x1f\x7b\x6d\x1c\x66\x2a\x1c\x66\x0c\xe9\x99\xc3\x8c\x4d\xb7\x28\xcf\xa2\x4c\x8b\xd4\x74\xa8\xc5\xa2\x1e\x8b\x02\x16\x99\x8c\xa9\xc0\x48\x90\x6a\xcb\x9d\xc9\x18\x9b\xcd\x18\xfb\xfb\xd8\x3c\xc5\xc3\x9f\xb5\x56\xee\x3e\x77\x9f\x3f\xcc\xc7\xda\x1f\x1f\x16\x5c\x7d\x5b\x70\x75\xa1\xe0\x6a\xc1\xef\x89\x6c\xde\xff\x2a\xb8\xfa\x37\x82\x2b\xb3\x05\x57\x72\x7a\x35\xc6\x0c\x2d\x79\x3e\x29\x0d\xfc\xdf\x16\x5c\x0d\x0a\xae\xc2\x1d\x22\x53\x70\xb5\x53\x70\xf5\x20\xed\xeb\x5c\xc9\xc7\x58\xc3\x48\x33\x3f\x97\x18\xbf\x42\xb1\x57\x5a\x84\x6d\x07\x60\x4a\xd2\x37\x6e\x5b\x97\xfd\x1b\xf5\x56\x2a\xb8\xfa\x24\xec\x67\x4c\xe7\xc3\x1b\x83\x83\x5f\x73\x05\x31\xc6\x0b\xd6\xbe\x45\xe7\xf2\x64\x14\x3c\xbb\x08\x8c\x8a\xa7\xfa\x71\x21\xc6\xc2\x1c\x30\x0e\x7e\x0d\x02\x27\xfe\x81\x78\x20\xe1\x3b\xd6\xc9\x78\x84\x39\xd8\xf5\xe2\x29\x60\x94\xcd\x04\xe1\xcd\x8a\x1d\x47\xa3\xe0\xea\x0b\x7e\xce\x5c\x7a\x8a\xfc\x64\x7c\xfc\x2c\x48\xf1\x75\xb8\x6f\x48\x9c\x37\xf8\x31\xdb\x4a\x64\x9c\x84\xb2\xbd\x19\xe0\x3f\xf2\x17\x60\xb6\xee\x85\x68\xe0\x26\x40\x34\x12\x13\x20\x46\xa8\x0e\xbf\x61\x1b\x6c\x8b\x7d\xf4\xe2\xc9\x10\x6a\x58\x0b\xe1\xcb\x5b\xc1\xa8\xfc\x46\xff\x9c\x70\xb5\x0d\xc7\xd0\xa3\x65\x33\x91\xe6\x3c\x0c\x17\x7f\xa4\xeb\x14\xc5\xb4\xc2\xc3\xe8\x3c\x43\xb1\x65\xb0\x3b\xe5\x98\xb1\x0d\xb6\xc5\x3e\xf2\x7c\x30\x13\x22\xdd\x35\x10\xf5\x5f\xa7\x73\xa9\x28\xbc\xc7\x1e\x43\x93\xe0\xca\x53\x14\x73\x16\xa6\x8e\x89\x86\x83\x9f\xce\x41\x55\xdf\x23\xec\x62\x77\x2e\x9d\xc7\xed\xb3\xa8\x1c\x5c\x10\xa2\xa2\x05\x22\xb7\x3e\x21\xc2\x77\xac\xeb\xff\x1e\xa2\x3e\xd8\x17\x79\x20\xaf\x68\xa8\x87\xda\x84\xea\x97\xcb\xbc\xb3\x1c\x43\xa5\xe0\xea\xe4\x74\x62\xba\xe1\xe0\x0f\x35\xac\x91\x36\xc0\xdd\x10\xac\x7b\xbb\x1f\x7b\x34\x0c\x66\x47\x05\xdd\x3d\xe9\xa5\x8f\x80\x5e\x34\x51\x52\xe9\x23\xf2\x3e\xaa\xa3\x82\xda\xd8\x63\x08\xd6\xfe\x52\xc6\xee\xde\x0c\xe2\x69\x8f\x1d\xcf\x4d\x92\xbf\x1a\x45\x7f\xeb\xf3\x32\x57\x5f\x0a\x3b\x4a\x17\x3f\xea\xd2\x28\x9f\x25\xf5\x76\xe4\x3b\xfd\x79\xad\x70\x1f\x84\xce\xff\x17\xe8\x7b\x26\x59\xfe\xc6\xca\xdb\xdb\x3e\x46\x63\xf4\x0d\xdb\xd8\xbc\xe9\x0c\x78\xe4\x79\xe2\x85\x3c\x69\x9e\x68\x7e\x7b\x21\x50\xfd\x63\x7b\x4d\x5f\x13\x5c\xfd\x7a\xca\x73\x45\x9a\xf8\x69\x7e\xad\xb5\x67\xde\xa8\xea\xd7\x59\xdd\x02\x79\x0f\xc1\xa5\x1f\xe9\xd9\x39\x06\x6e\x6e\xcd\x22\xc2\x77\xb9\x36\x5d\xd4\x06\xdb\xda\xf6\x64\xde\x38\x42\xbc\x90\x27\xf2\x76\x4c\xac\xbb\x86\x72\x44\xd6\x18\xde\xd3\xb9\xcb\x9d\xcc\x1f\xa5\x83\x9f\xf2\x5c\x15\x73\x48\x5f\x81\x4f\x5f\x75\xce\xa8\xe1\xcb\x5b\x1c\x9b\x45\xac\x57\xde\xcd\xa3\x5c\x66\xcd\xd2\x29\x44\xf8\x8e\x75\x72\x1c\x0a\xb5\xc5\x3e\x92\xa9\x49\xbc\x68\x0e\x2a\xe6\x0c\x38\x5b\x87\x2e\x2c\xb1\xce\x86\x6a\xbb\xe0\xea\x57\x93\x9f\x2d\x53\xe3\xa7\x5c\xcf\xae\x6c\xb2\x03\xb3\xb3\x5a\xea\xa9\xaf\x51\x9e\x5d\x35\x06\xdd\xdb\x33\x9c\x9c\x53\x3c\xc2\x6f\xd8\x86\xfc\x4e\xf9\x57\x9c\xfc\x9f\xd9\xf9\xb1\xb4\xbb\xc2\x6c\x30\xdb\xcb\xfb\xf5\xa5\x5f\x95\x79\x41\xb9\x67\x2c\x12\xdc\x9d\xf0\xce\x2a\x1d\xfc\xe4\xdf\xc8\x5f\x7c\x97\x72\xf1\x54\x57\xf3\xef\x64\x17\xbd\x1e\x37\xe5\x6c\x12\x61\xb7\x09\xdb\x60\x5b\xec\x83\x7d\xe5\x00\x0c\xe2\x89\xbc\x51\xc6\x00\x99\x75\x0b\x6d\x1b\x3a\x2e\xb8\x32\x61\xa4\xf8\xf1\x37\xe5\xe9\x35\x06\xa1\x86\x55\x96\x7e\x5a\xc0\x28\x9d\x49\x75\xad\xef\x8d\x1b\x82\xb5\x76\xd9\xc0\x7b\x00\x9b\xb0\xad\xed\xfb\xa3\xfa\x15\x69\x2b\x17\x57\x51\x1d\xca\x88\x95\x8d\xfe\x57\xde\x9f\x29\x3e\xc1\xd5\x6f\x26\xce\x8f\x24\xc7\x1f\xf1\x35\x80\x5e\xf2\x00\xe8\xbb\xf3\x21\x72\xf3\x98\x65\xf7\x1f\x80\xf0\x8e\x81\x9e\x1d\x63\xc8\xc6\x6d\x7c\x35\x4b\xa7\x92\xbd\xdf\xda\x96\x09\x5d\xdb\x33\xa1\x6d\x43\x8e\x73\xbf\x60\xe7\xf9\xb1\x0f\xfa\x48\xdc\x7b\x89\xff\xcd\x6a\xe2\x4d\x79\x65\x5f\xc3\x40\xbd\xfd\xee\x5b\xb6\x0d\x51\x7e\x3b\xde\x3a\x4e\x85\xdf\xec\x38\x44\xb6\x6f\x94\x3d\x46\xff\x67\xc0\x27\x70\xf2\x65\xe2\x7b\xed\xfd\x9c\x98\xbc\xe7\x54\xa9\x5f\x27\x16\x50\x40\xe7\x2a\x5c\xdf\x9c\x4d\xf7\x32\x76\x3b\xec\x83\x7d\x03\x9f\xfc\xa3\xc4\xe9\x6f\x27\xde\x28\x03\x65\x0d\xb0\xa1\x9a\xff\xb0\xf1\x6f\xd3\xb9\x2b\xee\x9d\x69\x2a\xfc\xa4\x6b\x9c\xdf\xc3\xcf\x01\x44\x02\x94\xcb\x35\x2a\x9f\xa6\xba\xa6\xb5\x13\x9c\x7c\x29\xae\x51\x9f\x47\xe6\x93\xf4\xc2\x6c\xd0\x77\x8f\x73\xe2\xcd\xcb\xeb\xf3\x9d\xfc\x2a\xf6\x21\x7e\x87\x9e\x96\x79\xe1\x48\x80\x78\x63\x1d\xca\x1a\x20\xfb\x2a\xb7\xf7\xb3\x6a\xc1\x95\xdc\x91\xe0\x0f\x5d\x58\x26\xfd\xe6\xf1\x97\xa4\xbe\xc4\x65\xca\x5d\xf5\xee\x54\x29\xe7\x6d\xeb\xb5\x0d\xf5\xca\x5d\x60\xec\x9d\x4a\x77\x30\x91\xda\x37\xc0\x5f\x3e\x93\xc6\xd3\xf9\xc1\x58\xb2\x2d\x3b\x4f\xee\xdb\xa9\x12\x0f\xe4\x45\xf3\x79\xfc\x25\x92\x81\xb2\x06\xd8\xee\xcd\x13\xf2\xce\x98\x2b\x4d\x82\xab\x0f\x26\xc9\x91\x3e\x97\x08\x3f\xed\x4f\x3b\x19\x04\x4f\xbd\x2e\x79\xf6\xd4\xd2\xbe\xd3\xbd\x7d\x0c\x9c\xb5\x6c\x1b\xb1\xe1\x7e\xa5\x7b\x55\x08\x1d\xfb\x11\x40\xf3\x5a\x22\xf3\xf4\xcf\x69\x2e\xd0\xff\xa3\xed\x23\x7e\xec\xd3\xbd\xdd\x4d\x31\x74\xa4\xa7\x4e\xca\x38\xf5\xba\x94\x81\xfb\x5b\xac\xef\x10\xcd\x74\xf7\x64\xdd\x79\xcf\x4a\x82\xff\x5b\x42\x63\x86\x71\xf0\xc9\x21\xb1\x24\xf9\x4e\xe4\x5d\x3b\xdf\xd2\xc9\x71\xd0\x8b\xf2\xa1\x6b\x5b\x86\xe3\x63\xb0\xec\xda\x9e\x41\xf7\x6f\x66\xcd\xeb\x00\xcd\x6b\x00\x9a\x56\x43\xb4\x7e\x31\x18\x7b\x27\x83\xcf\x23\xe7\x0a\xf1\x3b\x6d\x8b\xf2\xe9\xff\x3a\x24\xa3\x76\xbe\x94\x31\xc8\x87\x62\x6c\x4a\x31\x8b\xc6\xfa\x04\x57\x9f\x4e\x82\xff\x69\x6c\x43\xb1\x88\xff\xfa\xc8\xf0\x6f\xcb\x00\x7d\x57\x16\x98\x67\x7e\xde\x8f\xff\x42\x01\x18\xc5\x93\x08\xff\xf9\x91\xe0\x0f\x76\x03\xea\x14\x75\x4b\x3a\x4e\x8c\x7f\x16\xe5\x96\xf7\xcd\xa0\x39\x4b\x6e\x3f\x75\x34\xf7\x68\x03\xb6\xfd\xa0\xef\xb9\xb1\x85\xee\xd3\x21\x78\xe4\xcf\xe8\x0e\x10\xed\x27\x7c\xe2\x45\xd0\xbd\x19\xd0\xbd\x23\x83\xda\x0e\xdb\x7e\xc2\x7d\x74\x67\x81\xb6\x4d\x36\x9e\x18\xff\x83\xb8\x46\xf4\xa2\x09\x74\xdf\x31\x92\xf5\x7b\xf5\xb7\xb9\x96\xef\xc9\x81\x60\xd5\x73\x10\xaa\xfe\x3e\xe8\xc5\xf7\x82\xce\x15\xba\xd3\xc2\x31\x9e\xfa\xb5\xbd\x7e\x95\xb4\xd6\xef\x20\xfc\xdf\x8e\x8f\x9f\x72\xeb\xb9\xe4\xa3\x70\x5f\xb9\xca\x07\xf0\x48\xd7\x7f\x9e\x5b\x81\x7b\x53\x46\x7f\xec\x6c\x91\x4f\xeb\x8f\x2f\xb0\x6d\xe3\x30\xfc\x67\xba\xf8\xe5\xde\xa0\x6e\x47\x1e\xc1\x9a\xf9\x03\x78\xa4\xbb\x7f\x21\x35\xad\x1b\x0f\xbd\x9e\x31\xd6\x3d\xa6\x0a\x7d\x9a\x9b\xe6\x05\x75\x3f\x64\xff\x3a\xf9\xb2\xb5\x46\x13\xef\x5f\xe9\xe0\xd7\xed\xbb\x3e\xdc\xa3\x51\x2f\xbf\xfb\xd3\x81\x71\x48\xdc\xf8\x61\x6b\xdc\xf8\xc1\xde\xc7\x30\x6e\x40\x9c\xa8\xeb\x58\xec\xc3\x89\x1f\xd2\xc5\x1f\xb3\x06\xbe\x29\xb8\xd2\xa7\xef\xb9\x0f\x22\x5d\x9f\x0e\xe0\xe1\xc4\x6f\x17\xd3\x8b\xdf\x12\xd1\x70\xe2\xb7\xe1\xe1\xa7\x35\x30\x81\x62\x55\xcd\x45\xe7\xd0\xd8\xe7\xb3\x88\x9f\x87\x85\x5f\x53\x18\xfd\x3f\x95\xab\x8b\xe5\x19\xe3\x71\x3a\x43\x38\x6b\xe0\xf7\x76\x7e\xa9\x8e\x7b\x7e\x19\x2e\xfe\x18\x1b\xc2\xb3\x5a\x07\x9e\xdd\xf0\x0c\xe7\xf0\xb9\x23\xe7\xc7\x9b\xa3\xc4\x4f\x7e\xc8\x8d\x67\x66\x3a\xa7\xef\x7f\x88\xce\xd2\xf6\x93\xf6\xf9\x7d\x47\xcc\xf9\x7d\x47\xb2\xf3\x7b\x55\xdc\xf3\xfb\x48\xf1\xc7\xcc\xc1\xd7\x28\x77\x81\x3e\xae\xfa\xaf\x29\xa7\x01\x43\xf2\x27\xcf\x0f\xcc\x9f\x9c\xbb\x3d\xf9\x93\xd1\xe2\xef\xe3\x2e\xe6\xe3\x4c\x91\x67\x66\x35\x8a\x7e\x8e\xd6\xb2\xa5\xb3\xd4\xf9\xab\x17\xc1\x28\x7d\x04\xf4\x3d\x13\x89\x8c\x44\xf9\xab\xba\xb7\x87\xe6\xaf\x6e\x03\xfe\x98\x39\x98\x2c\xb8\x7a\xc8\xb6\xd9\x50\xfd\x32\x99\x1b\x0c\xf5\x38\xfe\xe2\xf6\xe5\x0f\x13\xe7\x4e\x47\x84\xbf\x50\xb5\xfd\xe9\x1c\x2b\x97\x4a\xb9\x55\xfa\xdf\x50\xe0\x3a\x44\xba\xcf\xc8\xdc\x37\xda\x8a\x9d\xbf\x4d\x82\xc1\xc1\x62\xe5\x6f\xb1\x0f\xe5\x7e\x4b\x1f\xa5\x5c\x70\xd2\x3e\x23\xc0\x4f\x63\xf0\xba\x58\x8f\x27\x1b\xe7\xe1\x05\x99\xd3\x56\x68\xbe\x8d\xca\x6f\xd0\xbe\x19\x6a\x58\xeb\xac\x3d\x27\x7f\xde\x56\x92\x7e\xfe\xbc\xe4\x41\x30\xdb\xf6\xa5\x1e\xf3\x08\xf1\xe3\xa3\x7b\x15\x66\xec\xa2\xff\x6d\xbc\x60\xdd\x2d\x48\xbc\xbb\xb2\xe8\xbf\x31\x7a\xf1\xd4\x24\xf7\x17\xf3\x89\x12\xdd\x5f\x18\x15\x4f\xd1\x1d\x48\xca\x7b\x92\xda\xb7\xac\xff\xda\xba\x86\x8d\x9f\xc6\xc0\x5d\x0c\xbc\xe4\x5b\xd1\x27\x95\x39\x77\x64\x8e\x8f\x19\xcd\xfd\xd1\xb0\xee\xa9\x46\x84\x1f\x1f\x43\x73\xd9\x6b\x7a\xbc\xbc\x6b\xa3\x3b\xb7\x4e\xeb\x0e\xee\x4e\xdd\xf7\x8d\x18\xbf\x33\x0e\x8f\xc2\x7a\x8b\x54\x8c\x35\x72\x04\x57\x67\x0b\xae\xfe\xc4\xba\x0b\x2d\xb8\x03\xf7\xae\x6f\x5b\x77\xbc\x23\xc6\x7f\xf7\xb9\xfb\x7c\x9e\x1e\xb9\x43\x24\x2e\x5b\x18\x63\xcf\x58\x65\x9e\x55\x66\x5a\xa5\x6b\x50\xc9\xec\xb2\xc0\x2a\x9f\x19\x54\x4e\x4f\x50\xe6\x25\x28\x33\x6f\x5f\xd9\x93\xa0\x0c\x24\x28\xcd\x41\x65\xd4\x2a\xc1\x2e\x17\x0e\x2a\x5b\xac\xb2\xc7\x2a\x4d\xab\x4c\xa1\xdf\xff\x0f\x00\x00\xff\xff\xc6\xb9\x24\x2f\xee\x3a\x00\x00") - -func faviconIcoBytes() ([]byte, error) { - return bindataRead( - _faviconIco, - "favicon.ico", - ) -} - -func faviconIco() (*asset, error) { - bytes, err := faviconIcoBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "favicon.ico", size: 15086, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _jsJquery211Js = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xce\xcf\x2b\xce\xcf\x49\xd5\xcb\xc9\x4f\xd7\x50\x4a\xad\x48\xcc\x2d\xc8\x49\x55\xd2\xb4\x06\x04\x00\x00\xff\xff\xc8\x9f\xbd\x5f\x17\x00\x00\x00") - -func jsJquery211JsBytes() ([]byte, error) { - return bindataRead( - _jsJquery211Js, - "js/jquery-2.1.1.js", - ) -} - -func jsJquery211Js() (*asset, error) { - bytes, err := jsJquery211JsBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "js/jquery-2.1.1.js", size: 23, mode: os.FileMode(438), modTime: time.Unix(1591451173, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "css/bootstrap.min.css": cssBootstrapMinCss, - "favicon.ico": faviconIco, - "js/jquery-2.1.1.js": jsJquery211Js, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"} -// AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("notexist") would return an error -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "css": {nil, map[string]*bintree{ - "bootstrap.min.css": {cssBootstrapMinCss, map[string]*bintree{}}, - }}, - "favicon.ico": {faviconIco, map[string]*bintree{}}, - "js": {nil, map[string]*bintree{ - "jquery-2.1.1.js": {jsJquery211Js, map[string]*bintree{}}, - }}, -}} - -// RestoreAsset restores an asset under the given directory -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil -} - -// RestoreAssets restores an asset under the given directory recursively -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) -} diff --git a/_examples/file-server/embedding-gzipped-files-into-app/main.go b/_examples/file-server/embedding-gzipped-files-into-app/main.go deleted file mode 100644 index a80814c21a..0000000000 --- a/_examples/file-server/embedding-gzipped-files-into-app/main.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -// How to run: -// -// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata -// $ go-bindata -prefix "../embedding-files-into-app/assets/" -fs ../embedding-files-into-app/assets/... -// $ go run . -// Time to complete the compression and caching of [2/3] files: 31.9998ms -// Total size reduced from 156.6 kB to: -// br (22.9 kB) [85.37%] -// snappy (41.7 kB) [73.37%] -// gzip (27.9 kB) [82.16%] -// deflate (27.9 kB) [82.19%] - -var dirOptions = iris.DirOptions{ - IndexName: "index.html", - // The `Compress` field is ignored - // when the file is cached (when Cache.Enable is true), - // because the cache file has a map of pre-compressed contents for each encoding - // that is served based on client's accept-encoding. - Compress: true, // true or false does not matter here. - Cache: iris.DirCacheOptions{ - Enable: true, - CompressIgnore: iris.MatchImagesAssets, - // Here, define the encodings that the cached files should be pre-compressed - // and served based on client's needs. - Encodings: []string{"gzip", "deflate", "br", "snappy"}, - CompressMinSize: 50, // files smaller than this size will NOT be compressed. - Verbose: 1, - }, -} - -func newApp() *iris.Application { - app := iris.New() - app.HandleDir("/static", AssetFile(), dirOptions) - return app -} - -func main() { - app := newApp() - - // http://localhost:8080/static/css/bootstrap.min.css - // http://localhost:8080/static/js/jquery-2.1.1.js - // http://localhost:8080/static/favicon.ico - app.Listen(":8080") -} diff --git a/_examples/file-server/embedding-gzipped-files-into-app/main_test.go b/_examples/file-server/embedding-gzipped-files-into-app/main_test.go deleted file mode 100644 index af4ac99fc3..0000000000 --- a/_examples/file-server/embedding-gzipped-files-into-app/main_test.go +++ /dev/null @@ -1,125 +0,0 @@ -package main - -import ( - "bytes" - "io/ioutil" - "path/filepath" - "runtime" - "strings" - "testing" - - "github.com/kataras/iris/v12/httptest" - "github.com/klauspost/compress/gzip" -) - -type resource string - -// content types that are used in the ./assets, -// we could use the detectContentType that iris do but it's better -// to do it manually so we can test if that returns the correct result on embedding files. -func (r resource) contentType() string { - switch filepath.Ext(r.String()) { - case ".js": - return "text/javascript" - case ".css": - return "text/css" - case ".ico": - return "image/x-icon" - case ".html": - return "text/html" - default: - return "text/plain" - } -} - -func (r resource) String() string { - return string(r) -} - -func (r resource) strip(strip string) string { - s := r.String() - return strings.TrimPrefix(s, strip) -} - -func (r resource) loadFromBase(dir string) string { - filename := r.String() - - filename = r.strip("/static") - - fullpath := filepath.Join(dir, filename) - - b, err := ioutil.ReadFile(fullpath) - if err != nil { - panic(fullpath + " failed with error: " + err.Error()) - } - result := string(b) - - if runtime.GOOS != "windows" { - result = strings.Replace(result, "\n", "\r\n", -1) - } - return result -} - -var urls = []resource{ - "/static/css/bootstrap.min.css", - "/static/js/jquery-2.1.1.js", - "/static/favicon.ico", -} - -// if bindata's values matches with the assets/... contents -// and secondly if the HandleDir had successfully registered -// the routes and gave the correct response. -func TestEmbeddingGzipFilesIntoApp(t *testing.T) { - dirOptions.Cache.Verbose = 0 - app := newApp() - - e := httptest.New(t, app) - - if runtime.GOOS != "windows" { - // remove the embedded static favicon for !windows, - // it should be built for unix-specific in order to be work - urls = urls[0 : len(urls)-1] - } - - for i, u := range urls { - url := u.String() - rawContents := u.loadFromBase("../embedding-files-into-app/assets") - shouldBeCompressed := int64(len(rawContents)) >= dirOptions.Cache.CompressMinSize - - request := e.GET(url) - if shouldBeCompressed { - request.WithHeader("Accept-Encoding", "gzip") - } - response := request.Expect() - response.ContentType(u.contentType(), app.ConfigurationReadOnly().GetCharset()) - if shouldBeCompressed { - response.ContentEncoding("gzip") - } - - if expected, got := response.Raw().StatusCode, httptest.StatusOK; expected != got { - t.Fatalf("[%d] of '%s': expected %d status code but got %d", i, url, expected, got) - } - rawBody := response.Body().Raw() - - if shouldBeCompressed { - reader, err := gzip.NewReader(strings.NewReader(rawBody)) - defer reader.Close() - if err != nil { - t.Fatalf("[%d] of '%s': %v", i, url, err) - } - buf := new(bytes.Buffer) - reader.WriteTo(buf) - if expected, got := rawContents, buf.String(); expected != got { - // t.Fatalf("[%d] of '%s': expected body:\n%s but got:\n%s", i, url, expected, got) - // let's reduce the output here... - // they are big files, no need to check for length here. - t.Fatalf("[%d] %s, expected body to look like: '%s...%s' but got '%s...%s'", i, url, expected[:40], expected[len(rawContents)-40:], got[:40], got[len(got)-40:]) - } - } else { - if expected, got := rawContents, rawBody; expected != got { - t.Fatalf("[%d] %s, expected body to look like: '%s...%s' but got '%s...%s'", i, url, expected[:40], expected[len(rawContents)-40:], got[:40], got[len(got)-40:]) - } - } - - } -} diff --git a/_examples/file-server/favicon/main.go b/_examples/file-server/favicon/main.go deleted file mode 100644 index f10f60888d..0000000000 --- a/_examples/file-server/favicon/main.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - // This will serve the ./static/favicons/favicon.ico to: localhost:8080/favicon.ico - app.Favicon("./static/favicons/favicon.ico") - - // app.Favicon("./static/favicons/favicon.\\.ico", "/favicon_16_16.ico") - // This will serve the ./static/favicons/favicon.ico to: localhost:8080/favicon_16_16.ico - - app.Get("/", func(ctx iris.Context) { - ctx.HTML(` press here to see the favicon.ico. - At some browsers like chrome, it should be visible at the top-left side of the browser's window, - because some browsers make requests to the /favicon.ico automatically, - so iris serves your favicon in that path too (you can change it).`) - }) // if favicon doesn't show to you, try to clear your browser's cache. - - app.Listen(":8080") -} diff --git a/_examples/file-server/favicon/static/favicons/favicon.ico b/_examples/file-server/favicon/static/favicons/favicon.ico deleted file mode 100644 index c370da518e..0000000000 Binary files a/_examples/file-server/favicon/static/favicons/favicon.ico and /dev/null differ diff --git a/_examples/file-server/file-server/main.go b/_examples/file-server/file-server/main.go deleted file mode 100644 index 3032a97659..0000000000 --- a/_examples/file-server/file-server/main.go +++ /dev/null @@ -1,135 +0,0 @@ -package main - -import ( - "crypto/md5" - "fmt" - "io" - "mime/multipart" - "os" - "path" - "strconv" - "strings" - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/basicauth" -) - -func init() { - os.Mkdir("./uploads", 0700) -} - -const ( - maxSize = 1 * iris.GB - uploadDir = "./uploads" -) - -func main() { - app := iris.New() - - view := iris.HTML("./views", ".html") - view.AddFunc("formatBytes", func(b int64) string { - const unit = 1000 - if b < unit { - return fmt.Sprintf("%d B", b) - } - div, exp := int64(unit), 0 - for n := b / unit; n >= unit; n /= unit { - div *= unit - exp++ - } - return fmt.Sprintf("%.1f %cB", - float64(b)/float64(div), "kMGTPE"[exp]) - }) - app.RegisterView(view) - - // Serve assets (e.g. javascript, css). - // app.HandleDir("/public", iris.Dir("./public")) - - app.Get("/", index) - - app.Get("/upload", uploadView) - app.Post("/upload", upload) - - filesRouter := app.Party("/files") - { - filesRouter.HandleDir("/", iris.Dir(uploadDir), iris.DirOptions{ - Compress: true, - ShowList: true, - - // Optionally, force-send files to the client inside of showing to the browser. - Attachments: iris.Attachments{ - Enable: true, - // Optionally, control data sent per second: - Limit: 50.0 * iris.KB, - Burst: 100 * iris.KB, - // Change the destination name through: - // NameFunc: func(systemName string) string {...} - }, - - DirList: iris.DirListRich(iris.DirListRichOptions{ - // Optionally, use a custom template for listing: - // Tmpl: dirListRichTemplate, - TmplName: "dirlist.html", - }), - }) - - auth := basicauth.New(basicauth.Config{ - Users: map[string]string{ - "myusername": "mypassword", - }, - }) - - filesRouter.Delete("/{file:path}", auth, deleteFile) - } - - app.Listen(":8080") -} - -func index(ctx iris.Context) { - ctx.Redirect("/upload") -} - -func uploadView(ctx iris.Context) { - now := time.Now().Unix() - h := md5.New() - io.WriteString(h, strconv.FormatInt(now, 10)) - token := fmt.Sprintf("%x", h.Sum(nil)) - - ctx.View("upload.html", token) -} - -func upload(ctx iris.Context) { - ctx.SetMaxRequestBodySize(maxSize) - - _, err := ctx.UploadFormFiles(uploadDir, beforeSave) - if err != nil { - ctx.StopWithError(iris.StatusPayloadTooRage, err) - return - } - - ctx.Redirect("/files") -} - -func beforeSave(ctx iris.Context, file *multipart.FileHeader) { - ip := ctx.RemoteAddr() - ip = strings.ReplaceAll(ip, ".", "_") - ip = strings.ReplaceAll(ip, ":", "_") - - file.Filename = ip + "-" + file.Filename -} - -func deleteFile(ctx iris.Context) { - // It does not contain the system path, - // as we are not exposing it to the user. - fileName := ctx.Params().Get("file") - - filePath := path.Join(uploadDir, fileName) - - if err := os.RemoveAll(filePath); err != nil { - ctx.StopWithError(iris.StatusInternalServerError, err) - return - } - - ctx.Redirect("/files") -} diff --git a/_examples/file-server/file-server/views/dirlist.html b/_examples/file-server/file-server/views/dirlist.html deleted file mode 100644 index 2b676f0c10..0000000000 --- a/_examples/file-server/file-server/views/dirlist.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - {{.Title}} - - - - - - - - - - - - - - - {{ range $idx, $file := .Files }} - - - {{ if $file.Download }} - - {{ else }} - - {{ end }} - {{ if $file.Info.IsDir }} - - {{ else }} - - {{ end }} - - - - {{ end }} - -
#NameSizeActions
{{ $idx }}{{ $file.Name }}{{ $file.Name }}Dir{{ formatBytes $file.Info.Size }}
- - - - \ No newline at end of file diff --git a/_examples/file-server/file-server/views/upload.html b/_examples/file-server/file-server/views/upload.html deleted file mode 100644 index a278be2ec3..0000000000 --- a/_examples/file-server/file-server/views/upload.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - Upload Files - - - -
- - - - -
- - - - - \ No newline at end of file diff --git a/_examples/file-server/http2push-embedded-gzipped/bindata.go b/_examples/file-server/http2push-embedded-gzipped/bindata.go deleted file mode 100644 index 78976fd86b..0000000000 --- a/_examples/file-server/http2push-embedded-gzipped/bindata.go +++ /dev/null @@ -1,580 +0,0 @@ -// Code generated by go-bindata. (@generated) DO NOT EDIT. - -//Package main generated by go-bindata.// sources: -// ../http2push/assets/app2/app2app3/css/main.css -// ../http2push/assets/app2/app2app3/dirs/dir1/text.txt -// ../http2push/assets/app2/app2app3/dirs/dir2/text.txt -// ../http2push/assets/app2/app2app3/dirs/text.txt -// ../http2push/assets/app2/app2app3/index.html -// ../http2push/assets/app2/index.html -// ../http2push/assets/app2/mydir/text.txt -// ../http2push/assets/css/main.css -// ../http2push/assets/favicon.ico -// ../http2push/assets/index.html -// ../http2push/assets/js/main.js -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data, name string) ([]byte, error) { - gz, err := gzip.NewReader(strings.NewReader(data)) - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -// Name return file name -func (fi bindataFileInfo) Name() string { - return fi.name -} - -// Size return file size -func (fi bindataFileInfo) Size() int64 { - return fi.size -} - -// Mode return file mode -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} - -// ModTime return file modify time -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} - -// IsDir return file whether a directory -func (fi bindataFileInfo) IsDir() bool { - return fi.mode&os.ModeDir != 0 -} - -// Sys return file is sys mode -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -type assetFile struct { - *bytes.Reader - name string - childInfos []os.FileInfo - childInfoOffset int -} - -type assetOperator struct{} - -// Open implement http.FileSystem interface -func (f *assetOperator) Open(name string) (http.File, error) { - var err error - if len(name) > 0 && name[0] == '/' { - name = name[1:] - } - content, err := Asset(name) - if err == nil { - return &assetFile{name: name, Reader: bytes.NewReader(content)}, nil - } - children, err := AssetDir(name) - if err == nil { - childInfos := make([]os.FileInfo, 0, len(children)) - for _, child := range children { - childPath := filepath.Join(name, child) - info, errInfo := AssetInfo(filepath.Join(name, child)) - if errInfo == nil { - childInfos = append(childInfos, info) - } else { - childInfos = append(childInfos, newDirFileInfo(childPath)) - } - } - return &assetFile{name: name, childInfos: childInfos}, nil - } else { - // If the error is not found, return an error that will - // result in a 404 error. Otherwise the server returns - // a 500 error for files not found. - if strings.Contains(err.Error(), "not found") { - return nil, os.ErrNotExist - } - return nil, err - } -} - -// Close no need do anything -func (f *assetFile) Close() error { - return nil -} - -// Readdir read dir's children file info -func (f *assetFile) Readdir(count int) ([]os.FileInfo, error) { - if len(f.childInfos) == 0 { - return nil, os.ErrNotExist - } - if count <= 0 { - return f.childInfos, nil - } - if f.childInfoOffset+count > len(f.childInfos) { - count = len(f.childInfos) - f.childInfoOffset - } - offset := f.childInfoOffset - f.childInfoOffset += count - return f.childInfos[offset : offset+count], nil -} - -// Stat read file info from asset item -func (f *assetFile) Stat() (os.FileInfo, error) { - if len(f.childInfos) != 0 { - return newDirFileInfo(f.name), nil - } - return AssetInfo(f.name) -} - -// newDirFileInfo return default dir file info -func newDirFileInfo(name string) os.FileInfo { - return &bindataFileInfo{ - name: name, - size: 0, - mode: os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir - modTime: time.Time{}} -} - -// AssetFile return a http.FileSystem instance that data backend by asset -func AssetFile() http.FileSystem { - return &assetOperator{} -} - -var _app2App2app3CssMainCss = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xca\x4f\xa9\x54\xa8\xe6\xe5\x52\x50\x50\x50\x48\x4a\x4c\xce\x4e\x2f\xca\x2f\xcd\x4b\xd1\x4d\xce\xcf\xc9\x2f\xb2\x52\x48\xca\x29\x4d\xb5\xe6\xe5\xaa\x05\x04\x00\x00\xff\xff\x52\xd7\xbb\x8b\x26\x00\x00\x00" - -func app2App2app3CssMainCssBytes() ([]byte, error) { - return bindataRead( - _app2App2app3CssMainCss, - "app2/app2app3/css/main.css", - ) -} - -func app2App2app3CssMainCss() (*asset, error) { - bytes, err := app2App2app3CssMainCssBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app2/app2app3/css/main.css", size: 38, mode: os.FileMode(438), modTime: time.Unix(1595043712, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _app2App2app3DirsDir1TextTxt = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\x2c\x28\x30\xd2\x07\x11\x89\x05\x05\xc6\xfa\x29\x99\x45\xc5\x20\xc2\x50\xbf\x24\xb5\xa2\x44\xaf\xa4\xa2\x04\x10\x00\x00\xff\xff\x87\xaf\x9d\x00\x20\x00\x00\x00" - -func app2App2app3DirsDir1TextTxtBytes() ([]byte, error) { - return bindataRead( - _app2App2app3DirsDir1TextTxt, - "app2/app2app3/dirs/dir1/text.txt", - ) -} - -func app2App2app3DirsDir1TextTxt() (*asset, error) { - bytes, err := app2App2app3DirsDir1TextTxtBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app2/app2app3/dirs/dir1/text.txt", size: 32, mode: os.FileMode(438), modTime: time.Unix(1594843207, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _app2App2app3DirsDir2TextTxt = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\x2c\x28\x30\xd2\x07\x11\x89\x05\x05\xc6\xfa\x29\x99\x45\xc5\x20\xc2\x48\xbf\x24\xb5\xa2\x44\xaf\xa4\xa2\x04\x10\x00\x00\xff\xff\x84\x14\xaa\xeb\x20\x00\x00\x00" - -func app2App2app3DirsDir2TextTxtBytes() ([]byte, error) { - return bindataRead( - _app2App2app3DirsDir2TextTxt, - "app2/app2app3/dirs/dir2/text.txt", - ) -} - -func app2App2app3DirsDir2TextTxt() (*asset, error) { - bytes, err := app2App2app3DirsDir2TextTxtBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app2/app2app3/dirs/dir2/text.txt", size: 32, mode: os.FileMode(438), modTime: time.Unix(1594843207, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _app2App2app3DirsTextTxt = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\x2c\x28\x30\xd2\x07\x11\x89\x05\x05\xc6\xfa\x29\x99\x45\xc5\xfa\x25\xa9\x15\x25\x7a\x25\x15\x25\x80\x00\x00\x00\xff\xff\x64\xfe\x96\xd6\x1b\x00\x00\x00" - -func app2App2app3DirsTextTxtBytes() ([]byte, error) { - return bindataRead( - _app2App2app3DirsTextTxt, - "app2/app2app3/dirs/text.txt", - ) -} - -func app2App2app3DirsTextTxt() (*asset, error) { - bytes, err := app2App2app3DirsTextTxtBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app2/app2app3/dirs/text.txt", size: 27, mode: os.FileMode(438), modTime: time.Unix(1594843207, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _app2App2app3IndexHtml = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x90\x3f\x4f\xc3\x40\x0c\xc5\xf7\x48\xf9\x0e\xc6\x33\xed\x91\x76\x61\xb8\x8b\x54\xf1\x47\x6c\x30\x94\x81\xf1\x7a\x31\x9c\x85\x73\x39\xe5\x4c\x4b\xbf\x3d\x4a\x68\x90\x58\x6c\x3d\xfb\xbd\x9f\x64\xdb\xab\xfb\xe7\xbb\xfd\xdb\xcb\x03\x44\xed\xa5\xad\x2b\x3b\x75\x10\x9f\x3e\x1c\x52\xc2\xb6\xae\xa6\x19\xf9\xae\xad\x2b\x00\x00\xdb\x93\x7a\x08\xd1\x8f\x85\xd4\xe1\xeb\xfe\x71\x75\x8b\xff\x76\xc9\xf7\xe4\xf0\xc8\x74\xca\xc3\xa8\x08\x61\x48\x4a\x49\x1d\x9e\xb8\xd3\xe8\x3a\x3a\x72\xa0\xd5\x2c\xae\x81\x13\x2b\x7b\x59\x95\xe0\x85\x5c\xb3\xbe\xf9\x63\x09\xa7\x4f\x18\x49\x1c\x16\x3d\x0b\x95\x48\xa4\x08\x71\xa4\x77\x87\x26\x7f\x1d\x84\x83\xf1\x39\x6f\xe6\xe2\x73\xde\x9a\x50\x8a\xe9\x3d\xa7\x75\x28\x05\xc1\x2c\x20\x65\x15\x6a\x77\x39\x6f\x76\x39\x6f\xad\xf9\xd5\x75\x65\xcd\xe5\xac\xba\xb2\x87\xa1\x3b\x2f\xfe\xd8\xb4\x4f\x24\x32\xc0\x12\x01\x4e\x1d\x7d\x5b\x13\x9b\x39\x75\xf1\xce\x80\xe9\x67\x3f\x01\x00\x00\xff\xff\xef\x25\x54\xc8\x43\x01\x00\x00" - -func app2App2app3IndexHtmlBytes() ([]byte, error) { - return bindataRead( - _app2App2app3IndexHtml, - "app2/app2app3/index.html", - ) -} - -func app2App2app3IndexHtml() (*asset, error) { - bytes, err := app2App2app3IndexHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app2/app2app3/index.html", size: 323, mode: os.FileMode(438), modTime: time.Unix(1595043725, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _app2IndexHtml = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\xc9\x30\xb4\xf3\x48\xcd\xc9\xc9\x57\x70\x2c\x28\x30\x52\xc8\xcc\x4b\x49\xad\xb0\xd1\xcf\x30\xb4\x03\x04\x00\x00\xff\xff\x75\x17\xab\xfa\x19\x00\x00\x00" - -func app2IndexHtmlBytes() ([]byte, error) { - return bindataRead( - _app2IndexHtml, - "app2/index.html", - ) -} - -func app2IndexHtml() (*asset, error) { - bytes, err := app2IndexHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app2/index.html", size: 25, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _app2MydirTextTxt = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xca\x2a\x2d\x2e\x51\x48\x54\x28\x49\xad\x28\xd1\x03\x04\x00\x00\xff\xff\x2f\xf9\x22\x98\x0c\x00\x00\x00" - -func app2MydirTextTxtBytes() ([]byte, error) { - return bindataRead( - _app2MydirTextTxt, - "app2/mydir/text.txt", - ) -} - -func app2MydirTextTxt() (*asset, error) { - bytes, err := app2MydirTextTxtBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app2/mydir/text.txt", size: 12, mode: os.FileMode(438), modTime: time.Unix(1594787248, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _cssMainCss = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xca\x4f\xa9\x54\xa8\xe6\xe5\x52\x50\x50\x50\x48\x4a\x4c\xce\x4e\x2f\xca\x2f\xcd\x4b\xd1\x4d\xce\xcf\xc9\x2f\xb2\x52\x48\xca\x49\x4c\xce\xb6\xe6\xe5\xaa\xe5\xe5\x02\x04\x00\x00\xff\xff\x03\x25\x9c\x89\x29\x00\x00\x00" - -func cssMainCssBytes() ([]byte, error) { - return bindataRead( - _cssMainCss, - "css/main.css", - ) -} - -func cssMainCss() (*asset, error) { - bytes, err := cssMainCssBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "css/main.css", size: 41, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _faviconIco = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5a\x0b\x70\x55\xc7\x79\xde\x7b\xce\x45\x12\xb2\x90\xc4\xc3\xe6\x61\x3b\x90\xf8\x31\xc4\x19\x6c\x32\xe3\xc4\x34\xe3\xc6\x34\x6d\xed\xd4\x69\x62\x32\x49\x9a\xa6\x75\xea\x36\x33\xee\xd8\x9e\xb4\x75\xdc\x7a\xa6\xc5\x31\x02\xa6\xd3\x84\x47\x28\x6f\x3b\xc6\x3c\xcc\xeb\x9e\xbd\x08\x21\x04\x92\x28\x12\x0e\x08\x5b\x3c\xec\x02\x92\x78\x08\x21\x09\x41\x25\x24\x10\xe8\x71\xb5\xe7\xdc\xd7\xb9\xf7\xef\xfc\xff\x9e\x73\x74\x25\xdd\x97\x24\x82\x33\x1e\xce\xcc\x3f\x7b\xee\x9e\xdd\xff\xff\xf6\xdf\x7f\xff\xfd\xf7\xdf\xcb\x98\x8b\xa9\x2c\x3f\x1f\xcb\x19\xec\x15\x37\x63\x5f\x67\x8c\xcd\x98\x21\x7f\x6b\xf9\x8c\x6d\x72\x33\x36\x7b\xb6\xf5\xfb\x11\xc6\x9e\xbd\x97\xb1\x99\x8c\xb1\x7c\x6c\xc7\x64\x3d\x3d\x6e\x76\xdb\x1f\xe1\x75\xab\x42\x53\x7e\x22\x3c\x6c\x91\xf0\xb0\x02\x22\xae\x14\x08\x8f\xab\x40\xec\x64\x92\xf0\x5d\x53\xfe\x59\xec\x64\x5f\x15\x1e\xa6\x08\x4f\x7f\x7f\xdf\x7a\x96\xa1\x97\x3c\xb8\x3f\x78\xfa\x0d\x08\x9e\x5d\x08\xc1\xda\x5f\x82\x51\xf6\x65\x30\xca\x67\x41\xf0\xcc\xbf\x11\x19\x07\x1e\x07\xbd\x78\x2a\x18\x65\x8f\xb5\x0b\x4d\x7d\xad\x6f\x07\x73\x0b\xcd\x45\xfd\xbb\x17\xb3\x0c\xa3\x62\xce\xfe\xa8\xd1\x0a\xf8\x98\x6d\x25\x10\x38\xfe\x53\x88\xdc\x3a\x01\x66\x6b\x11\x11\xbe\x07\x8e\xfd\x2d\x84\x2e\x2c\x05\xff\xd1\x79\x3d\xc2\xc3\x7e\x20\x78\x06\x13\x5c\x89\xe9\xdf\x06\xd1\x50\x2f\x04\x4e\xfc\x0c\xcc\x6b\xa5\x60\x1c\xfd\x21\x74\x6c\x1e\x47\x84\xef\x58\x17\xf8\xe4\x65\x30\x6f\x54\x81\x71\xe0\x89\xea\xbe\x2d\x6c\xd2\x80\xfe\xfe\x76\x30\x6f\x1c\x81\x60\xed\x7c\x08\x9e\x5d\x0c\x2d\xeb\xc7\x41\xed\xd2\x29\x50\xbb\x74\x32\xbd\x63\x1d\x7e\xc3\x36\xa1\x73\xff\x19\x14\x1e\x36\x4f\x78\xc7\xc4\xf4\xef\x80\x70\xd3\xfb\x10\x6e\x5c\x0f\xdd\x65\x73\xe1\xdc\xf2\x89\xd0\xb1\x25\x0f\x6e\x7c\x90\x07\xe7\x7f\x33\x81\xea\xf0\x5b\xb8\x79\x13\x61\xd0\x8b\xa7\x2c\x09\x7c\xfc\x57\xac\x7b\x11\xf6\x7f\x6a\x5f\xd4\xb8\x06\xa1\xfa\x15\x10\x6e\x7a\x0f\x3a\xb4\xc7\xa0\x61\x55\x3e\x04\x8f\xfc\x39\x84\x3e\xfa\x4b\x68\x5c\x93\x4f\x75\xf8\x2d\x74\x71\x05\x44\x45\x13\xea\x54\xeb\x7c\x85\xa9\x48\xf8\x1e\x15\xcd\xf4\xcd\xee\x7f\x71\x65\x3e\x04\x3e\x7c\x86\x78\x5c\x5a\x1d\xd3\xbf\x7e\x05\xa0\x2c\x94\x89\xb2\x03\xd5\x3f\x66\x88\x05\x31\x21\x36\x1b\xff\xd9\xe5\x13\xe1\xda\xc6\x5c\x68\xdf\x94\x4b\x63\x71\xf0\x37\xbd\x0f\x38\x56\x1c\x33\x8e\x1d\x75\x80\xba\x40\x9d\x0c\xd6\x5f\xcd\x92\x29\x44\x83\xf5\x87\xba\x76\xfa\x73\x85\xe1\x5c\x18\x07\x9e\x38\x86\x18\x68\x8e\xac\xf9\xbb\xbe\x39\x87\xc8\x99\xbf\x13\x3f\xa3\x39\xc6\xb9\x8e\xed\x4f\xb6\xe0\x61\x3f\x40\xdb\x40\x1b\x41\x5b\x19\x62\x3f\xc7\x7f\x4a\xb6\x85\x0f\xda\x9a\xdd\x9f\xd6\x80\xe6\x62\xd2\x26\xd5\xd7\xd0\x46\xc9\x56\x0f\x3c\xde\x6f\xbf\xe5\xb3\xc8\xa6\xd1\xb6\xc9\xc6\x4f\xbf\x01\x68\xf3\x68\xfb\xce\x3a\xf2\x30\x26\x76\x32\x85\xd6\x08\xae\x95\xc1\xeb\x87\xd6\x14\xb3\x69\x11\xad\x39\xaf\x5b\x1d\xed\xfa\x05\x60\x8c\x65\x32\xc6\x54\x8b\x5c\x31\x64\x3d\x0b\x63\xe8\xb0\x45\x2d\x56\xdf\x99\x96\x8f\x99\x1b\xeb\x67\xf2\x47\x8b\xea\xf3\xf9\x08\xae\x22\xe5\x09\xae\x4e\x17\x5c\xfd\xc2\x28\x69\x8a\xe0\xca\x58\xb4\x3d\xdb\x17\xa6\x29\xff\x5f\x04\x57\x5b\x85\xe6\xba\x92\x90\xb8\x1a\x43\x4a\x4c\xbd\x62\xd7\xb7\x08\xae\xd6\x0b\xae\xfe\x8f\xe0\xea\x9b\x38\x1e\x1f\x1f\x4b\xfe\x34\xb5\x7c\xa5\x40\xec\xca\x02\xbd\x64\x3a\xe8\xfb\xbe\x38\x94\x4a\xa6\x83\xf0\x66\x82\xd0\x14\x10\x9a\x0b\x44\xe1\x38\x5c\x2b\x44\xf8\x4e\x75\x5c\x01\xe1\x75\x03\xf2\x11\x9a\x2b\x2a\xb8\x52\x2b\xb8\x3a\x4f\x68\x8a\x92\x0c\x03\xc9\xf7\xb0\x02\xa3\xfc\x2b\x10\xe9\xae\x85\xa8\x7e\x15\xa2\xfa\x95\x18\xba\x0a\xa1\xb3\x8b\x49\xbe\xbe\xe7\x5e\x08\xfe\xef\x3f\x81\xd9\x51\x09\x91\xbe\x4b\x44\xf8\x8e\x75\xf8\x4d\x70\x37\x04\x3e\xfe\x11\xf9\x14\xbd\x68\x3c\xe2\xe8\x14\x5c\x7d\x49\x78\x99\x2b\x11\x06\x47\xfe\x81\xd9\x10\x0d\xde\x82\xc1\x8f\x79\xe3\x30\xe8\x7b\xa7\x81\x51\xfa\x28\x98\xd7\xca\x00\xa2\xa6\xfc\x80\x65\xcc\x3b\x7e\xc3\x36\xfa\xde\x07\x08\x93\xd9\x5a\x0c\xfa\xfe\x87\x41\x68\xac\x43\x70\xf5\x59\x92\xe3\x1d\xea\x1a\x92\xc9\x8f\x06\x3a\xc1\xff\xe1\x5c\xda\x67\x91\x27\x3e\x91\xde\x0b\xe4\xaf\xfc\x47\x5f\x20\xc2\x77\xac\x23\xac\x1d\x95\xd4\xd6\xff\xe1\x9f\x10\x2f\xfa\x5d\xf2\x05\xc4\x70\x4c\x70\xf5\x7e\x94\x35\x1c\xf9\xa1\xc6\x77\x68\x3e\x43\x17\x57\x4a\xfe\xe8\xc3\xcb\xbe\x8c\xfc\xa0\xcf\x23\x09\xdf\xb1\x0e\xbf\x51\x9f\x8b\x2b\x65\x9f\xc6\x77\xe8\x37\xee\x4f\x62\xd7\x58\xb4\x8f\x05\x3e\x9e\x35\xc4\x1e\x13\xc9\x8f\x06\xbb\xc0\xa8\xfc\x23\xf0\x1f\xfa\x63\x88\x86\xba\x21\x72\xeb\x24\x18\xfb\x1f\x82\xde\x1d\x2e\x68\xdd\x30\x0e\x1a\x56\x4f\x24\xc2\x77\xac\xc3\x6f\xd8\x06\xdb\x62\x1f\xec\x8b\xef\x10\xd6\xc1\xff\xd1\xf7\x11\x67\x83\xe0\xea\x97\x06\xeb\x20\x91\x7c\xd2\xdd\xee\x3c\x08\x37\x6f\x04\x30\x0d\x08\x7c\x34\x8f\xe4\x34\xae\x99\x00\xa7\x7f\x3d\x0d\x4e\xfd\xea\x7e\x22\x7c\xc7\x3a\xfc\x86\x6d\xb0\x6d\xb8\x69\x23\xf5\xb5\xe7\xcc\x6c\x3f\x00\x62\x77\x6e\x54\x68\xae\x57\xad\xf5\x96\x52\x7e\xb0\x6e\x01\xe8\xfb\x66\xd0\x1a\x30\xaf\x95\x83\x28\xcc\x81\x2b\xef\xe6\x92\x3c\x24\x8a\x63\x96\x4d\x71\x7e\xe3\x37\x6c\x83\x6d\xb1\x0f\xf6\x0d\xd6\x15\x48\x5d\x86\x7c\x64\x47\xc2\xc3\xf6\x0a\xae\x66\xc6\xea\x20\xae\xfc\x48\x08\xfc\x55\xdf\x05\x7f\xd5\x77\x00\x22\x41\x08\x7e\xfa\x2a\x74\x6d\x53\xa1\x6e\xf9\x64\x92\x75\x69\xcd\x44\xb8\xb9\x35\x13\x6e\x6d\xcb\x80\xe6\x75\xe3\xa9\x0e\xbf\x61\x1b\x6c\x8b\x7d\xb0\xaf\xbf\xea\x7b\xc4\x8b\xec\xe2\xec\x22\x9c\x83\x66\xc1\xd5\x19\xa9\xe4\xd3\xdc\x1f\x98\x0d\xc1\x33\x6f\x42\x34\xec\x03\x7f\xe5\x1c\x68\xdb\x30\x96\xe4\x9c\xfb\xcd\x7d\xd0\xbd\x63\x0c\xe8\x85\x63\x41\x2f\xbc\x07\x7a\x3d\x6e\xa8\x5f\x39\x89\xbe\x61\x1b\x6c\x8b\x7d\xb0\xaf\xe4\xd9\x65\xd9\x6e\x19\x88\x5d\xd9\xba\xd0\x5c\x73\x53\xca\xd7\xff\x0f\xf4\xfd\x0f\x41\xa8\x61\x35\xf9\x1f\x7d\xdf\x74\x68\x5e\x97\x47\xf3\xdd\xf2\x4e\x1e\xc9\x0d\x9f\xfc\x3b\x30\x4f\xbd\x02\xc6\x9e\x09\xd0\xba\x21\x07\x4e\xfd\x6a\x1a\xb5\xc1\xb6\xe4\xb3\x1a\x56\x13\x0f\xe4\x25\xd7\xed\x39\x5c\x9b\x51\xa1\xb1\x17\xe3\xc8\x7f\x0b\x63\x13\x5c\xef\xd4\xb6\xaf\x91\x7c\x6b\xf8\xf2\x16\x5a\xdb\x62\xcf\x64\xb8\xb4\x7a\x3c\xc9\x68\xdb\x90\x0d\xfe\xb2\x87\x01\x2e\x2d\x07\x68\x5a\x05\x81\xca\x27\xe1\xfa\xe6\x4c\x39\x2f\xab\xc7\x53\xdb\x88\xaf\x9e\xfa\x22\x0f\xe4\xe5\xc4\x47\xa5\x8f\xa2\x0d\xfc\x22\x8e\xfc\x5f\xe0\x37\x3b\x5e\x4f\x2e\xff\x1e\xf0\x97\x7e\x11\xa2\x0d\x4b\x00\x9a\x56\x42\xe0\xe0\x13\x70\x7d\x53\x56\x6a\xf9\x81\x4e\x8a\xbf\x70\xac\x43\xe4\x6b\xec\x45\xd4\x0d\xea\x28\xbe\xfe\x67\x38\xfa\x6f\x5e\x97\x2f\xfd\xcb\xd1\xe7\x21\x74\xec\x87\xa0\xef\xce\x81\xab\xbf\x1d\x67\x7d\xcb\xb3\xd6\xcc\x50\xfd\xe3\xdc\xe2\x1c\xcb\xb3\xd0\x60\xf9\xae\xb9\x68\x1b\xe4\xdf\x53\xd8\x1f\xda\x39\xda\xbe\xee\x55\x41\xf7\xba\xc9\x16\xcf\xaf\xb8\x2f\xa5\xfd\x25\x95\x8f\x6b\x42\x63\xcd\xb8\xcf\x25\x5c\x7f\x5b\xe5\xfa\xc3\x39\xb8\xf0\xdf\xf7\x42\xfb\xc6\x7b\xa0\x63\x53\x36\x5c\x5c\x35\x29\xad\xf5\x97\x42\x7e\x26\xfa\x06\xda\x37\xc2\xbe\xb4\xfc\x0f\xd2\x99\x25\xd3\xd2\xf2\x3f\xc9\xe5\x2b\xf6\x1c\xbc\x86\x3e\x12\x7d\xe5\x50\xff\xeb\x8f\xe3\x7f\x25\x0d\xf5\xbf\xfe\x21\xfe\x37\x99\xfc\x18\x1d\x7c\x09\xf7\x08\xdc\x2b\xc0\xd4\x69\xef\x30\x2a\x46\xb9\xff\x58\x73\x9f\x5a\xbe\xc2\x7c\x1a\xed\x8d\x0b\x70\xaf\xc4\x3d\x53\xee\xbf\xef\x8e\x7a\xff\x4d\x47\x7e\x8c\x0e\xee\xc7\x58\x01\x63\x06\xd4\x1d\xf6\x41\x9b\x18\x51\xfc\x61\xf9\xb2\xb4\xe5\x7b\x55\x1b\xc3\xb3\x18\x33\x61\xec\x84\x31\x14\xf1\xdc\xfb\x40\x5a\xf1\x97\x4e\xf1\xd7\x34\x8a\xd9\x06\x3f\xa9\xe4\x3b\xb6\x48\xb1\xa2\xfa\x12\xc6\x8e\x18\x43\x62\x2c\x89\x31\x25\xc6\x96\x29\xe3\x4f\x6f\x26\xc5\xaa\xf1\x62\x58\x8c\x6d\x31\xc6\x4d\x26\xbf\x7f\x3d\x60\xcc\xac\xce\xa3\x18\x1a\x63\x69\x8c\xa9\xb9\x5b\xc6\xd8\x14\x7f\xe7\xc4\xc4\xdf\x39\xb2\x0e\x63\x73\x8c\x91\x93\xc5\xf0\xc4\x47\x49\x2a\x9f\x30\x68\x0a\xf3\x15\x65\x31\x6b\xaf\x7e\xd3\x3a\x53\xd4\x5b\x67\x8c\x44\xe7\x0f\x49\xc9\xcf\x30\xad\xd6\x59\x27\xa9\xfc\x58\x5d\xf4\x69\x14\x2f\x8d\x95\x67\xab\x51\x9f\xcf\xa6\x5b\x67\xbd\xb4\xe4\x7f\xde\x1f\x7b\x6d\x1c\x66\x2a\x1c\x66\x0c\xe9\x99\xc3\x8c\x4d\xb7\x28\xcf\xa2\x4c\x8b\xd4\x74\xa8\xc5\xa2\x1e\x8b\x02\x16\x99\x8c\xa9\xc0\x48\x90\x6a\xcb\x9d\xc9\x18\x9b\xcd\x18\xfb\xfb\xd8\x3c\xc5\xc3\x9f\xb5\x56\xee\x3e\x77\x9f\x3f\xcc\xc7\xda\x1f\x1f\x16\x5c\x7d\x5b\x70\x75\xa1\xe0\x6a\xc1\xef\x89\x6c\xde\xff\x2a\xb8\xfa\x37\x82\x2b\xb3\x05\x57\x72\x7a\x35\xc6\x0c\x2d\x79\x3e\x29\x0d\xfc\xdf\x16\x5c\x0d\x0a\xae\xc2\x1d\x22\x53\x70\xb5\x53\x70\xf5\x20\xed\xeb\x5c\xc9\xc7\x58\xc3\x48\x33\x3f\x97\x18\xbf\x42\xb1\x57\x5a\x84\x6d\x07\x60\x4a\xd2\x37\x6e\x5b\x97\xfd\x1b\xf5\x56\x2a\xb8\xfa\x24\xec\x67\x4c\xe7\xc3\x1b\x83\x83\x5f\x73\x05\x31\xc6\x0b\xd6\xbe\x45\xe7\xf2\x64\x14\x3c\xbb\x08\x8c\x8a\xa7\xfa\x71\x21\xc6\xc2\x1c\x30\x0e\x7e\x0d\x02\x27\xfe\x81\x78\x20\xe1\x3b\xd6\xc9\x78\x84\x39\xd8\xf5\xe2\x29\x60\x94\xcd\x04\xe1\xcd\x8a\x1d\x47\xa3\xe0\xea\x0b\x7e\xce\x5c\x7a\x8a\xfc\x64\x7c\xfc\x2c\x48\xf1\x75\xb8\x6f\x48\x9c\x37\xf8\x31\xdb\x4a\x64\x9c\x84\xb2\xbd\x19\xe0\x3f\xf2\x17\x60\xb6\xee\x85\x68\xe0\x26\x40\x34\x12\x13\x20\x46\xa8\x0e\xbf\x61\x1b\x6c\x8b\x7d\xf4\xe2\xc9\x10\x6a\x58\x0b\xe1\xcb\x5b\xc1\xa8\xfc\x46\xff\x9c\x70\xb5\x0d\xc7\xd0\xa3\x65\x33\x91\xe6\x3c\x0c\x17\x7f\xa4\xeb\x14\xc5\xb4\xc2\xc3\xe8\x3c\x43\xb1\x65\xb0\x3b\xe5\x98\xb1\x0d\xb6\xc5\x3e\xf2\x7c\x30\x13\x22\xdd\x35\x10\xf5\x5f\xa7\x73\xa9\x28\xbc\xc7\x1e\x43\x93\xe0\xca\x53\x14\x73\x16\xa6\x8e\x89\x86\x83\x9f\xce\x41\x55\xdf\x23\xec\x62\x77\x2e\x9d\xc7\xed\xb3\xa8\x1c\x5c\x10\xa2\xa2\x05\x22\xb7\x3e\x21\xc2\x77\xac\xeb\xff\x1e\xa2\x3e\xd8\x17\x79\x20\xaf\x68\xa8\x87\xda\x84\xea\x97\xcb\xbc\xb3\x1c\x43\xa5\xe0\xea\xe4\x74\x62\xba\xe1\xe0\x0f\x35\xac\x91\x36\xc0\xdd\x10\xac\x7b\xbb\x1f\x7b\x34\x0c\x66\x47\x05\xdd\x3d\xe9\xa5\x8f\x80\x5e\x34\x51\x52\xe9\x23\xf2\x3e\xaa\xa3\x82\xda\xd8\x63\x08\xd6\xfe\x52\xc6\xee\xde\x0c\xe2\x69\x8f\x1d\xcf\x4d\x92\xbf\x1a\x45\x7f\xeb\xf3\x32\x57\x5f\x0a\x3b\x4a\x17\x3f\xea\xd2\x28\x9f\x25\xf5\x76\xe4\x3b\xfd\x79\xad\x70\x1f\x84\xce\xff\x17\xe8\x7b\x26\x59\xfe\xc6\xca\xdb\xdb\x3e\x46\x63\xf4\x0d\xdb\xd8\xbc\xe9\x0c\x78\xe4\x79\xe2\x85\x3c\x69\x9e\x68\x7e\x7b\x21\x50\xfd\x63\x7b\x4d\x5f\x13\x5c\xfd\x7a\xca\x73\x45\x9a\xf8\x69\x7e\xad\xb5\x67\xde\xa8\xea\xd7\x59\xdd\x02\x79\x0f\xc1\xa5\x1f\xe9\xd9\x39\x06\x6e\x6e\xcd\x22\xc2\x77\xb9\x36\x5d\xd4\x06\xdb\xda\xf6\x64\xde\x38\x42\xbc\x90\x27\xf2\x76\x4c\xac\xbb\x86\x72\x44\xd6\x18\xde\xd3\xb9\xcb\x9d\xcc\x1f\xa5\x83\x9f\xf2\x5c\x15\x73\x48\x5f\x81\x4f\x5f\x75\xce\xa8\xe1\xcb\x5b\x1c\x9b\x45\xac\x57\xde\xcd\xa3\x5c\x66\xcd\xd2\x29\x44\xf8\x8e\x75\x72\x1c\x0a\xb5\xc5\x3e\x92\xa9\x49\xbc\x68\x0e\x2a\xe6\x0c\x38\x5b\x87\x2e\x2c\xb1\xce\x86\x6a\xbb\xe0\xea\x57\x93\x9f\x2d\x53\xe3\xa7\x5c\xcf\xae\x6c\xb2\x03\xb3\xb3\x5a\xea\xa9\xaf\x51\x9e\x5d\x35\x06\xdd\xdb\x33\x9c\x9c\x53\x3c\xc2\x6f\xd8\x86\xfc\x4e\xf9\x57\x9c\xfc\x9f\xd9\xf9\xb1\xb4\xbb\xc2\x6c\x30\xdb\xcb\xfb\xf5\xa5\x5f\x95\x79\x41\xb9\x67\x2c\x12\xdc\x9d\xf0\xce\x2a\x1d\xfc\xe4\xdf\xc8\x5f\x7c\x97\x72\xf1\x54\x57\xf3\xef\x64\x17\xbd\x1e\x37\xe5\x6c\x12\x61\xb7\x09\xdb\x60\x5b\xec\x83\x7d\xe5\x00\x0c\xe2\x89\xbc\x51\xc6\x00\x99\x75\x0b\x6d\x1b\x3a\x2e\xb8\x32\x61\xa4\xf8\xf1\x37\xe5\xe9\x35\x06\xa1\x86\x55\x96\x7e\x5a\xc0\x28\x9d\x49\x75\xad\xef\x8d\x1b\x82\xb5\x76\xd9\xc0\x7b\x00\x9b\xb0\xad\xed\xfb\xa3\xfa\x15\x69\x2b\x17\x57\x51\x1d\xca\x88\x95\x8d\xfe\x57\xde\x9f\x29\x3e\xc1\xd5\x6f\x26\xce\x8f\x24\xc7\x1f\xf1\x35\x80\x5e\xf2\x00\xe8\xbb\xf3\x21\x72\xf3\x98\x65\xf7\x1f\x80\xf0\x8e\x81\x9e\x1d\x63\xc8\xc6\x6d\x7c\x35\x4b\xa7\x92\xbd\xdf\xda\x96\x09\x5d\xdb\x33\xa1\x6d\x43\x8e\x73\xbf\x60\xe7\xf9\xb1\x0f\xfa\x48\xdc\x7b\x89\xff\xcd\x6a\xe2\x4d\x79\x65\x5f\xc3\x40\xbd\xfd\xee\x5b\xb6\x0d\x51\x7e\x3b\xde\x3a\x4e\x85\xdf\xec\x38\x44\xb6\x6f\x94\x3d\x46\xff\x67\xc0\x27\x70\xf2\x65\xe2\x7b\xed\xfd\x9c\x98\xbc\xe7\x54\xa9\x5f\x27\x16\x50\x40\xe7\x2a\x5c\xdf\x9c\x4d\xf7\x32\x76\x3b\xec\x83\x7d\x03\x9f\xfc\xa3\xc4\xe9\x6f\x27\xde\x28\x03\x65\x0d\xb0\xa1\x9a\xff\xb0\xf1\x6f\xd3\xb9\x2b\xee\x9d\x69\x2a\xfc\xa4\x6b\x9c\xdf\xc3\xcf\x01\x44\x02\x94\xcb\x35\x2a\x9f\xa6\xba\xa6\xb5\x13\x9c\x7c\x29\xae\x51\x9f\x47\xe6\x93\xf4\xc2\x6c\xd0\x77\x8f\x73\xe2\xcd\xcb\xeb\xf3\x9d\xfc\x2a\xf6\x21\x7e\x87\x9e\x96\x79\xe1\x48\x80\x78\x63\x1d\xca\x1a\x20\xfb\x2a\xb7\xf7\xb3\x6a\xc1\x95\xdc\x91\xe0\x0f\x5d\x58\x26\xfd\xe6\xf1\x97\xa4\xbe\xc4\x65\xca\x5d\xf5\xee\x54\x29\xe7\x6d\xeb\xb5\x0d\xf5\xca\x5d\x60\xec\x9d\x4a\x77\x30\x91\xda\x37\xc0\x5f\x3e\x93\xc6\xd3\xf9\xc1\x58\xb2\x2d\x3b\x4f\xee\xdb\xa9\x12\x0f\xe4\x45\xf3\x79\xfc\x25\x92\x81\xb2\x06\xd8\xee\xcd\x13\xf2\xce\x98\x2b\x4d\x82\xab\x0f\x26\xc9\x91\x3e\x97\x08\x3f\xed\x4f\x3b\x19\x04\x4f\xbd\x2e\x79\xf6\xd4\xd2\xbe\xd3\xbd\x7d\x0c\x9c\xb5\x6c\x1b\xb1\xe1\x7e\xa5\x7b\x55\x08\x1d\xfb\x11\x40\xf3\x5a\x22\xf3\xf4\xcf\x69\x2e\xd0\xff\xa3\xed\x23\x7e\xec\xd3\xbd\xdd\x4d\x31\x74\xa4\xa7\x4e\xca\x38\xf5\xba\x94\x81\xfb\x5b\xac\xef\x10\xcd\x74\xf7\x64\xdd\x79\xcf\x4a\x82\xff\x5b\x42\x63\x86\x71\xf0\xc9\x21\xb1\x24\xf9\x4e\xe4\x5d\x3b\xdf\xd2\xc9\x71\xd0\x8b\xf2\xa1\x6b\x5b\x86\xe3\x63\xb0\xec\xda\x9e\x41\xf7\x6f\x66\xcd\xeb\x00\xcd\x6b\x00\x9a\x56\x43\xb4\x7e\x31\x18\x7b\x27\x83\xcf\x23\xe7\x0a\xf1\x3b\x6d\x8b\xf2\xe9\xff\x3a\x24\xa3\x76\xbe\x94\x31\xc8\x87\x62\x6c\x4a\x31\x8b\xc6\xfa\x04\x57\x9f\x4e\x82\xff\x69\x6c\x43\xb1\x88\xff\xfa\xc8\xf0\x6f\xcb\x00\x7d\x57\x16\x98\x67\x7e\xde\x8f\xff\x42\x01\x18\xc5\x93\x08\xff\xf9\x91\xe0\x0f\x76\x03\xea\x14\x75\x4b\x3a\x4e\x8c\x7f\x16\xe5\x96\xf7\xcd\xa0\x39\x4b\x6e\x3f\x75\x34\xf7\x68\x03\xb6\xfd\xa0\xef\xb9\xb1\x85\xee\xd3\x21\x78\xe4\xcf\xe8\x0e\x10\xed\x27\x7c\xe2\x45\xd0\xbd\x19\xd0\xbd\x23\x83\xda\x0e\xdb\x7e\xc2\x7d\x74\x67\x81\xb6\x4d\x36\x9e\x18\xff\x83\xb8\x46\xf4\xa2\x09\x74\xdf\x31\x92\xf5\x7b\xf5\xb7\xb9\x96\xef\xc9\x81\x60\xd5\x73\x10\xaa\xfe\x3e\xe8\xc5\xf7\x82\xce\x15\xba\xd3\xc2\x31\x9e\xfa\xb5\xbd\x7e\x95\xb4\xd6\xef\x20\xfc\xdf\x8e\x8f\x9f\x72\xeb\xb9\xe4\xa3\x70\x5f\xb9\xca\x07\xf0\x48\xd7\x7f\x9e\x5b\x81\x7b\x53\x46\x7f\xec\x6c\x91\x4f\xeb\x8f\x2f\xb0\x6d\xe3\x30\xfc\x67\xba\xf8\xe5\xde\xa0\x6e\x47\x1e\xc1\x9a\xf9\x03\x78\xa4\xbb\x7f\x21\x35\xad\x1b\x0f\xbd\x9e\x31\xd6\x3d\xa6\x0a\x7d\x9a\x9b\xe6\x05\x75\x3f\x64\xff\x3a\xf9\xb2\xb5\x46\x13\xef\x5f\xe9\xe0\xd7\xed\xbb\x3e\xdc\xa3\x51\x2f\xbf\xfb\xd3\x81\x71\x48\xdc\xf8\x61\x6b\xdc\xf8\xc1\xde\xc7\x30\x6e\x40\x9c\xa8\xeb\x58\xec\xc3\x89\x1f\xd2\xc5\x1f\xb3\x06\xbe\x29\xb8\xd2\xa7\xef\xb9\x0f\x22\x5d\x9f\x0e\xe0\xe1\xc4\x6f\x17\xd3\x8b\xdf\x12\xd1\x70\xe2\xb7\xe1\xe1\xa7\x35\x30\x81\x62\x55\xcd\x45\xe7\xd0\xd8\xe7\xb3\x88\x9f\x87\x85\x5f\x53\x18\xfd\x3f\x95\xab\x8b\xe5\x19\xe3\x71\x3a\x43\x38\x6b\xe0\xf7\x76\x7e\xa9\x8e\x7b\x7e\x19\x2e\xfe\x18\x1b\xc2\xb3\x5a\x07\x9e\xdd\xf0\x0c\xe7\xf0\xb9\x23\xe7\xc7\x9b\xa3\xc4\x4f\x7e\xc8\x8d\x67\x66\x3a\xa7\xef\x7f\x88\xce\xd2\xf6\x93\xf6\xf9\x7d\x47\xcc\xf9\x7d\x47\xb2\xf3\x7b\x55\xdc\xf3\xfb\x48\xf1\xc7\xcc\xc1\xd7\x28\x77\x81\x3e\xae\xfa\xaf\x29\xa7\x01\x43\xf2\x27\xcf\x0f\xcc\x9f\x9c\xbb\x3d\xf9\x93\xd1\xe2\xef\xe3\x2e\xe6\xe3\x4c\x91\x67\x66\x35\x8a\x7e\x8e\xd6\xb2\xa5\xb3\xd4\xf9\xab\x17\xc1\x28\x7d\x04\xf4\x3d\x13\x89\x8c\x44\xf9\xab\xba\xb7\x87\xe6\xaf\x6e\x03\xfe\x98\x39\x98\x2c\xb8\x7a\xc8\xb6\xd9\x50\xfd\x32\x99\x1b\x0c\xf5\x38\xfe\xe2\xf6\xe5\x0f\x13\xe7\x4e\x47\x84\xbf\x50\xb5\xfd\xe9\x1c\x2b\x97\x4a\xb9\x55\xfa\xdf\x50\xe0\x3a\x44\xba\xcf\xc8\xdc\x37\xda\x8a\x9d\xbf\x4d\x82\xc1\xc1\x62\xe5\x6f\xb1\x0f\xe5\x7e\x4b\x1f\xa5\x5c\x70\xd2\x3e\x23\xc0\x4f\x63\xf0\xba\x58\x8f\x27\x1b\xe7\xe1\x05\x99\xd3\x56\x68\xbe\x8d\xca\x6f\xd0\xbe\x19\x6a\x58\xeb\xac\x3d\x27\x7f\xde\x56\x92\x7e\xfe\xbc\xe4\x41\x30\xdb\xf6\xa5\x1e\xf3\x08\xf1\xe3\xa3\x7b\x15\x66\xec\xa2\xff\x6d\xbc\x60\xdd\x2d\x48\xbc\xbb\xb2\xe8\xbf\x31\x7a\xf1\xd4\x24\xf7\x17\xf3\x89\x12\xdd\x5f\x18\x15\x4f\xd1\x1d\x48\xca\x7b\x92\xda\xb7\xac\xff\xda\xba\x86\x8d\x9f\xc6\xc0\x5d\x0c\xbc\xe4\x5b\xd1\x27\x95\x39\x77\x64\x8e\x8f\x19\xcd\xfd\xd1\xb0\xee\xa9\x46\x84\x1f\x1f\x43\x73\xd9\x6b\x7a\xbc\xbc\x6b\xa3\x3b\xb7\x4e\xeb\x0e\xee\x4e\xdd\xf7\x8d\x18\xbf\x33\x0e\x8f\xc2\x7a\x8b\x54\x8c\x35\x72\x04\x57\x67\x0b\xae\xfe\xc4\xba\x0b\x2d\xb8\x03\xf7\xae\x6f\x5b\x77\xbc\x23\xc6\x7f\xf7\xb9\xfb\x7c\x9e\x1e\xb9\x43\x24\x2e\x5b\x18\x63\xcf\x58\x65\x9e\x55\x66\x5a\xa5\x6b\x50\xc9\xec\xb2\xc0\x2a\x9f\x19\x54\x4e\x4f\x50\xe6\x25\x28\x33\x6f\x5f\xd9\x93\xa0\x0c\x24\x28\xcd\x41\x65\xd4\x2a\xc1\x2e\x17\x0e\x2a\x5b\xac\xb2\xc7\x2a\x4d\xab\x4c\xa1\xdf\xff\x0f\x00\x00\xff\xff\xc6\xb9\x24\x2f\xee\x3a\x00\x00" - -func faviconIcoBytes() ([]byte, error) { - return bindataRead( - _faviconIco, - "favicon.ico", - ) -} - -func faviconIco() (*asset, error) { - bytes, err := faviconIcoBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "favicon.ico", size: 15086, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _indexHtml = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x64\x91\x3d\x4f\xc3\x30\x10\x86\xf7\x48\xf9\x0f\xd7\x9b\x40\x22\x35\x6c\x0c\x71\x96\x42\x57\x90\x28\x03\xa3\xeb\x5c\x9b\x6b\x1d\x27\xb2\x2f\x69\xfb\xef\x51\x3e\xca\xe7\x64\xdf\xeb\xbb\xc7\x8f\xec\x7c\xf1\xf4\xb2\xda\x7c\xbc\x3e\x43\x25\xb5\x2b\xd2\x24\x1f\x56\x70\xc6\xef\x35\x92\xc7\x22\x4d\x86\x8c\x4c\x59\xa4\x09\x00\x40\x5e\x93\x18\xb0\x95\x09\x91\x44\xe3\xfb\x66\x9d\x3d\xe2\xaf\x33\x6f\x6a\xd2\xd8\x33\x9d\xda\x26\x08\x82\x6d\xbc\x90\x17\x8d\x27\x2e\xa5\xd2\x25\xf5\x6c\x29\x1b\x8b\x3b\x60\xcf\xc2\xc6\x65\xd1\x1a\x47\xfa\x61\x79\xff\xc5\x72\xec\x8f\x10\xc8\x69\x64\xdb\x78\x84\x2a\xd0\x4e\xa3\x6a\xbb\xad\x63\xab\x76\xa6\x1f\xe2\x25\xdb\x06\x41\x2e\x2d\x69\xe4\xda\xec\x49\x9d\xb3\xa9\x5d\xfd\xe7\x44\xb9\x38\x8a\x15\x91\xfc\xa5\xd9\x18\x55\x6d\xd8\x2f\x6d\x8c\x3f\x46\x85\xc5\x51\xb1\x66\x47\xf0\x46\xa1\xa7\x90\xab\x29\x4a\x93\x5c\xcd\x6f\x92\x26\xf9\xb6\x29\x2f\xd7\x11\xf6\x6d\x27\xb3\xd0\xb6\x13\x19\x54\x1a\x6f\x1d\xdb\xa3\xc6\xc6\xaf\x86\xcd\xcd\x2d\x42\x6f\x5c\x47\x1a\xc7\x1a\x6a\x5a\x4c\xb7\xce\x90\x68\x03\xb7\x57\x8a\xd0\x59\xd4\xc1\xf4\x66\x4a\x11\x62\xb0\xdf\xe6\x87\x59\xfc\x10\xb1\xc8\xd5\xd4\x32\xea\xcd\x52\xa3\xe9\xf0\xb3\x9f\x01\x00\x00\xff\xff\x00\xcf\x96\xa8\xe9\x01\x00\x00" - -func indexHtmlBytes() ([]byte, error) { - return bindataRead( - _indexHtml, - "index.html", - ) -} - -func indexHtml() (*asset, error) { - bytes, err := indexHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "index.html", size: 489, mode: os.FileMode(438), modTime: time.Unix(1594886229, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _jsMainJs = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xce\xcf\x2b\xce\xcf\x49\xd5\xcb\xc9\x4f\xd7\x50\x4a\xad\x48\xcc\x2d\xc8\x49\x55\xd2\xb4\xe6\xe5\xe2\xe5\x4a\x2b\xcd\x4b\x2e\xc9\xcc\xcf\x53\xc8\xcf\x73\xce\xc9\x4c\xce\xd6\xd0\x54\xa8\xe6\xe5\x52\x50\x50\x50\x28\xcf\xcc\x4b\xc9\x2f\xd7\x4b\xcc\x49\x2d\x2a\xd1\x50\x4a\x2a\x2d\x29\xc9\xcf\x53\x48\x06\xa9\x49\x4d\x01\x6b\xae\x05\x04\x00\x00\xff\xff\xa4\xb7\x99\x52\x57\x00\x00\x00" - -func jsMainJsBytes() ([]byte, error) { - return bindataRead( - _jsMainJs, - "js/main.js", - ) -} - -func jsMainJs() (*asset, error) { - bytes, err := jsMainJsBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "js/main.js", size: 87, mode: os.FileMode(438), modTime: time.Unix(1594787764, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "app2/app2app3/css/main.css": app2App2app3CssMainCss, - "app2/app2app3/dirs/dir1/text.txt": app2App2app3DirsDir1TextTxt, - "app2/app2app3/dirs/dir2/text.txt": app2App2app3DirsDir2TextTxt, - "app2/app2app3/dirs/text.txt": app2App2app3DirsTextTxt, - "app2/app2app3/index.html": app2App2app3IndexHtml, - "app2/index.html": app2IndexHtml, - "app2/mydir/text.txt": app2MydirTextTxt, - "css/main.css": cssMainCss, - "favicon.ico": faviconIco, - "index.html": indexHtml, - "js/main.js": jsMainJs, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"} -// AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("notexist") would return an error -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "app2": {nil, map[string]*bintree{ - "app2app3": {nil, map[string]*bintree{ - "css": {nil, map[string]*bintree{ - "main.css": {app2App2app3CssMainCss, map[string]*bintree{}}, - }}, - "dirs": {nil, map[string]*bintree{ - "dir1": {nil, map[string]*bintree{ - "text.txt": {app2App2app3DirsDir1TextTxt, map[string]*bintree{}}, - }}, - "dir2": {nil, map[string]*bintree{ - "text.txt": {app2App2app3DirsDir2TextTxt, map[string]*bintree{}}, - }}, - "text.txt": {app2App2app3DirsTextTxt, map[string]*bintree{}}, - }}, - "index.html": {app2App2app3IndexHtml, map[string]*bintree{}}, - }}, - "index.html": {app2IndexHtml, map[string]*bintree{}}, - "mydir": {nil, map[string]*bintree{ - "text.txt": {app2MydirTextTxt, map[string]*bintree{}}, - }}, - }}, - "css": {nil, map[string]*bintree{ - "main.css": {cssMainCss, map[string]*bintree{}}, - }}, - "favicon.ico": {faviconIco, map[string]*bintree{}}, - "index.html": {indexHtml, map[string]*bintree{}}, - "js": {nil, map[string]*bintree{ - "main.js": {jsMainJs, map[string]*bintree{}}, - }}, -}} - -// RestoreAsset restores an asset under the given directory -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil -} - -// RestoreAssets restores an asset under the given directory recursively -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) -} diff --git a/_examples/file-server/http2push-embedded-gzipped/main.go b/_examples/file-server/http2push-embedded-gzipped/main.go deleted file mode 100644 index 8714d1374a..0000000000 --- a/_examples/file-server/http2push-embedded-gzipped/main.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "regexp" - - "github.com/kataras/iris/v12" -) - -// How to run: -// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata -// $ go-bindata -nomemcopy -fs -prefix "../http2push/assets" ../http2push/assets/... -// $ go run . - -var opts = iris.DirOptions{ - IndexName: "index.html", - PushTargetsRegexp: map[string]*regexp.Regexp{ - "/": iris.MatchCommonAssets, - "/app2/app2app3": iris.MatchCommonAssets, - }, - ShowList: true, - Cache: iris.DirCacheOptions{ - Enable: true, - CompressIgnore: iris.MatchImagesAssets, - // Here, define the encodings that the cached files should be pre-compressed - // and served based on client's needs. - Encodings: []string{"gzip", "deflate", "br", "snappy"}, - CompressMinSize: 50, // files smaller than this size will NOT be compressed. - Verbose: 1, - }, -} - -func main() { - app := iris.New() - app.HandleDir("/public", AssetFile(), opts) - - // https://127.0.0.1/public - // https://127.0.0.1/public/app2 - // https://127.0.0.1/public/app2/app2app3 - // https://127.0.0.1/public/app2/app2app3/dirs - app.Run(iris.TLS(":443", "../http2push/mycert.crt", "../http2push/mykey.key")) -} diff --git a/_examples/file-server/http2push-embedded/bindata.go b/_examples/file-server/http2push-embedded/bindata.go deleted file mode 100644 index 78976fd86b..0000000000 --- a/_examples/file-server/http2push-embedded/bindata.go +++ /dev/null @@ -1,580 +0,0 @@ -// Code generated by go-bindata. (@generated) DO NOT EDIT. - -//Package main generated by go-bindata.// sources: -// ../http2push/assets/app2/app2app3/css/main.css -// ../http2push/assets/app2/app2app3/dirs/dir1/text.txt -// ../http2push/assets/app2/app2app3/dirs/dir2/text.txt -// ../http2push/assets/app2/app2app3/dirs/text.txt -// ../http2push/assets/app2/app2app3/index.html -// ../http2push/assets/app2/index.html -// ../http2push/assets/app2/mydir/text.txt -// ../http2push/assets/css/main.css -// ../http2push/assets/favicon.ico -// ../http2push/assets/index.html -// ../http2push/assets/js/main.js -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data, name string) ([]byte, error) { - gz, err := gzip.NewReader(strings.NewReader(data)) - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -// Name return file name -func (fi bindataFileInfo) Name() string { - return fi.name -} - -// Size return file size -func (fi bindataFileInfo) Size() int64 { - return fi.size -} - -// Mode return file mode -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} - -// ModTime return file modify time -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} - -// IsDir return file whether a directory -func (fi bindataFileInfo) IsDir() bool { - return fi.mode&os.ModeDir != 0 -} - -// Sys return file is sys mode -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -type assetFile struct { - *bytes.Reader - name string - childInfos []os.FileInfo - childInfoOffset int -} - -type assetOperator struct{} - -// Open implement http.FileSystem interface -func (f *assetOperator) Open(name string) (http.File, error) { - var err error - if len(name) > 0 && name[0] == '/' { - name = name[1:] - } - content, err := Asset(name) - if err == nil { - return &assetFile{name: name, Reader: bytes.NewReader(content)}, nil - } - children, err := AssetDir(name) - if err == nil { - childInfos := make([]os.FileInfo, 0, len(children)) - for _, child := range children { - childPath := filepath.Join(name, child) - info, errInfo := AssetInfo(filepath.Join(name, child)) - if errInfo == nil { - childInfos = append(childInfos, info) - } else { - childInfos = append(childInfos, newDirFileInfo(childPath)) - } - } - return &assetFile{name: name, childInfos: childInfos}, nil - } else { - // If the error is not found, return an error that will - // result in a 404 error. Otherwise the server returns - // a 500 error for files not found. - if strings.Contains(err.Error(), "not found") { - return nil, os.ErrNotExist - } - return nil, err - } -} - -// Close no need do anything -func (f *assetFile) Close() error { - return nil -} - -// Readdir read dir's children file info -func (f *assetFile) Readdir(count int) ([]os.FileInfo, error) { - if len(f.childInfos) == 0 { - return nil, os.ErrNotExist - } - if count <= 0 { - return f.childInfos, nil - } - if f.childInfoOffset+count > len(f.childInfos) { - count = len(f.childInfos) - f.childInfoOffset - } - offset := f.childInfoOffset - f.childInfoOffset += count - return f.childInfos[offset : offset+count], nil -} - -// Stat read file info from asset item -func (f *assetFile) Stat() (os.FileInfo, error) { - if len(f.childInfos) != 0 { - return newDirFileInfo(f.name), nil - } - return AssetInfo(f.name) -} - -// newDirFileInfo return default dir file info -func newDirFileInfo(name string) os.FileInfo { - return &bindataFileInfo{ - name: name, - size: 0, - mode: os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir - modTime: time.Time{}} -} - -// AssetFile return a http.FileSystem instance that data backend by asset -func AssetFile() http.FileSystem { - return &assetOperator{} -} - -var _app2App2app3CssMainCss = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xca\x4f\xa9\x54\xa8\xe6\xe5\x52\x50\x50\x50\x48\x4a\x4c\xce\x4e\x2f\xca\x2f\xcd\x4b\xd1\x4d\xce\xcf\xc9\x2f\xb2\x52\x48\xca\x29\x4d\xb5\xe6\xe5\xaa\x05\x04\x00\x00\xff\xff\x52\xd7\xbb\x8b\x26\x00\x00\x00" - -func app2App2app3CssMainCssBytes() ([]byte, error) { - return bindataRead( - _app2App2app3CssMainCss, - "app2/app2app3/css/main.css", - ) -} - -func app2App2app3CssMainCss() (*asset, error) { - bytes, err := app2App2app3CssMainCssBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app2/app2app3/css/main.css", size: 38, mode: os.FileMode(438), modTime: time.Unix(1595043712, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _app2App2app3DirsDir1TextTxt = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\x2c\x28\x30\xd2\x07\x11\x89\x05\x05\xc6\xfa\x29\x99\x45\xc5\x20\xc2\x50\xbf\x24\xb5\xa2\x44\xaf\xa4\xa2\x04\x10\x00\x00\xff\xff\x87\xaf\x9d\x00\x20\x00\x00\x00" - -func app2App2app3DirsDir1TextTxtBytes() ([]byte, error) { - return bindataRead( - _app2App2app3DirsDir1TextTxt, - "app2/app2app3/dirs/dir1/text.txt", - ) -} - -func app2App2app3DirsDir1TextTxt() (*asset, error) { - bytes, err := app2App2app3DirsDir1TextTxtBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app2/app2app3/dirs/dir1/text.txt", size: 32, mode: os.FileMode(438), modTime: time.Unix(1594843207, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _app2App2app3DirsDir2TextTxt = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\x2c\x28\x30\xd2\x07\x11\x89\x05\x05\xc6\xfa\x29\x99\x45\xc5\x20\xc2\x48\xbf\x24\xb5\xa2\x44\xaf\xa4\xa2\x04\x10\x00\x00\xff\xff\x84\x14\xaa\xeb\x20\x00\x00\x00" - -func app2App2app3DirsDir2TextTxtBytes() ([]byte, error) { - return bindataRead( - _app2App2app3DirsDir2TextTxt, - "app2/app2app3/dirs/dir2/text.txt", - ) -} - -func app2App2app3DirsDir2TextTxt() (*asset, error) { - bytes, err := app2App2app3DirsDir2TextTxtBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app2/app2app3/dirs/dir2/text.txt", size: 32, mode: os.FileMode(438), modTime: time.Unix(1594843207, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _app2App2app3DirsTextTxt = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\x2c\x28\x30\xd2\x07\x11\x89\x05\x05\xc6\xfa\x29\x99\x45\xc5\xfa\x25\xa9\x15\x25\x7a\x25\x15\x25\x80\x00\x00\x00\xff\xff\x64\xfe\x96\xd6\x1b\x00\x00\x00" - -func app2App2app3DirsTextTxtBytes() ([]byte, error) { - return bindataRead( - _app2App2app3DirsTextTxt, - "app2/app2app3/dirs/text.txt", - ) -} - -func app2App2app3DirsTextTxt() (*asset, error) { - bytes, err := app2App2app3DirsTextTxtBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app2/app2app3/dirs/text.txt", size: 27, mode: os.FileMode(438), modTime: time.Unix(1594843207, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _app2App2app3IndexHtml = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x90\x3f\x4f\xc3\x40\x0c\xc5\xf7\x48\xf9\x0e\xc6\x33\xed\x91\x76\x61\xb8\x8b\x54\xf1\x47\x6c\x30\x94\x81\xf1\x7a\x31\x9c\x85\x73\x39\xe5\x4c\x4b\xbf\x3d\x4a\x68\x90\x58\x6c\x3d\xfb\xbd\x9f\x64\xdb\xab\xfb\xe7\xbb\xfd\xdb\xcb\x03\x44\xed\xa5\xad\x2b\x3b\x75\x10\x9f\x3e\x1c\x52\xc2\xb6\xae\xa6\x19\xf9\xae\xad\x2b\x00\x00\xdb\x93\x7a\x08\xd1\x8f\x85\xd4\xe1\xeb\xfe\x71\x75\x8b\xff\x76\xc9\xf7\xe4\xf0\xc8\x74\xca\xc3\xa8\x08\x61\x48\x4a\x49\x1d\x9e\xb8\xd3\xe8\x3a\x3a\x72\xa0\xd5\x2c\xae\x81\x13\x2b\x7b\x59\x95\xe0\x85\x5c\xb3\xbe\xf9\x63\x09\xa7\x4f\x18\x49\x1c\x16\x3d\x0b\x95\x48\xa4\x08\x71\xa4\x77\x87\x26\x7f\x1d\x84\x83\xf1\x39\x6f\xe6\xe2\x73\xde\x9a\x50\x8a\xe9\x3d\xa7\x75\x28\x05\xc1\x2c\x20\x65\x15\x6a\x77\x39\x6f\x76\x39\x6f\xad\xf9\xd5\x75\x65\xcd\xe5\xac\xba\xb2\x87\xa1\x3b\x2f\xfe\xd8\xb4\x4f\x24\x32\xc0\x12\x01\x4e\x1d\x7d\x5b\x13\x9b\x39\x75\xf1\xce\x80\xe9\x67\x3f\x01\x00\x00\xff\xff\xef\x25\x54\xc8\x43\x01\x00\x00" - -func app2App2app3IndexHtmlBytes() ([]byte, error) { - return bindataRead( - _app2App2app3IndexHtml, - "app2/app2app3/index.html", - ) -} - -func app2App2app3IndexHtml() (*asset, error) { - bytes, err := app2App2app3IndexHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app2/app2app3/index.html", size: 323, mode: os.FileMode(438), modTime: time.Unix(1595043725, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _app2IndexHtml = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\xc9\x30\xb4\xf3\x48\xcd\xc9\xc9\x57\x70\x2c\x28\x30\x52\xc8\xcc\x4b\x49\xad\xb0\xd1\xcf\x30\xb4\x03\x04\x00\x00\xff\xff\x75\x17\xab\xfa\x19\x00\x00\x00" - -func app2IndexHtmlBytes() ([]byte, error) { - return bindataRead( - _app2IndexHtml, - "app2/index.html", - ) -} - -func app2IndexHtml() (*asset, error) { - bytes, err := app2IndexHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app2/index.html", size: 25, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _app2MydirTextTxt = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xca\x2a\x2d\x2e\x51\x48\x54\x28\x49\xad\x28\xd1\x03\x04\x00\x00\xff\xff\x2f\xf9\x22\x98\x0c\x00\x00\x00" - -func app2MydirTextTxtBytes() ([]byte, error) { - return bindataRead( - _app2MydirTextTxt, - "app2/mydir/text.txt", - ) -} - -func app2MydirTextTxt() (*asset, error) { - bytes, err := app2MydirTextTxtBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app2/mydir/text.txt", size: 12, mode: os.FileMode(438), modTime: time.Unix(1594787248, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _cssMainCss = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xca\x4f\xa9\x54\xa8\xe6\xe5\x52\x50\x50\x50\x48\x4a\x4c\xce\x4e\x2f\xca\x2f\xcd\x4b\xd1\x4d\xce\xcf\xc9\x2f\xb2\x52\x48\xca\x49\x4c\xce\xb6\xe6\xe5\xaa\xe5\xe5\x02\x04\x00\x00\xff\xff\x03\x25\x9c\x89\x29\x00\x00\x00" - -func cssMainCssBytes() ([]byte, error) { - return bindataRead( - _cssMainCss, - "css/main.css", - ) -} - -func cssMainCss() (*asset, error) { - bytes, err := cssMainCssBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "css/main.css", size: 41, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _faviconIco = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5a\x0b\x70\x55\xc7\x79\xde\x7b\xce\x45\x12\xb2\x90\xc4\xc3\xe6\x61\x3b\x90\xf8\x31\xc4\x19\x6c\x32\xe3\xc4\x34\xe3\xc6\x34\x6d\xed\xd4\x69\x62\x32\x49\x9a\xa6\x75\xea\x36\x33\xee\xd8\x9e\xb4\x75\xdc\x7a\xa6\xc5\x31\x02\xa6\xd3\x84\x47\x28\x6f\x3b\xc6\x3c\xcc\xeb\x9e\xbd\x08\x21\x04\x92\x28\x12\x0e\x08\x5b\x3c\xec\x02\x92\x78\x08\x21\x09\x41\x25\x24\x10\xe8\x71\xb5\xe7\xdc\xd7\xb9\xf7\xef\xfc\xff\x9e\x73\x74\x25\xdd\x97\x24\x82\x33\x1e\xce\xcc\x3f\x7b\xee\x9e\xdd\xff\xff\xf6\xdf\x7f\xff\xfd\xf7\xdf\xcb\x98\x8b\xa9\x2c\x3f\x1f\xcb\x19\xec\x15\x37\x63\x5f\x67\x8c\xcd\x98\x21\x7f\x6b\xf9\x8c\x6d\x72\x33\x36\x7b\xb6\xf5\xfb\x11\xc6\x9e\xbd\x97\xb1\x99\x8c\xb1\x7c\x6c\xc7\x64\x3d\x3d\x6e\x76\xdb\x1f\xe1\x75\xab\x42\x53\x7e\x22\x3c\x6c\x91\xf0\xb0\x02\x22\xae\x14\x08\x8f\xab\x40\xec\x64\x92\xf0\x5d\x53\xfe\x59\xec\x64\x5f\x15\x1e\xa6\x08\x4f\x7f\x7f\xdf\x7a\x96\xa1\x97\x3c\xb8\x3f\x78\xfa\x0d\x08\x9e\x5d\x08\xc1\xda\x5f\x82\x51\xf6\x65\x30\xca\x67\x41\xf0\xcc\xbf\x11\x19\x07\x1e\x07\xbd\x78\x2a\x18\x65\x8f\xb5\x0b\x4d\x7d\xad\x6f\x07\x73\x0b\xcd\x45\xfd\xbb\x17\xb3\x0c\xa3\x62\xce\xfe\xa8\xd1\x0a\xf8\x98\x6d\x25\x10\x38\xfe\x53\x88\xdc\x3a\x01\x66\x6b\x11\x11\xbe\x07\x8e\xfd\x2d\x84\x2e\x2c\x05\xff\xd1\x79\x3d\xc2\xc3\x7e\x20\x78\x06\x13\x5c\x89\xe9\xdf\x06\xd1\x50\x2f\x04\x4e\xfc\x0c\xcc\x6b\xa5\x60\x1c\xfd\x21\x74\x6c\x1e\x47\x84\xef\x58\x17\xf8\xe4\x65\x30\x6f\x54\x81\x71\xe0\x89\xea\xbe\x2d\x6c\xd2\x80\xfe\xfe\x76\x30\x6f\x1c\x81\x60\xed\x7c\x08\x9e\x5d\x0c\x2d\xeb\xc7\x41\xed\xd2\x29\x50\xbb\x74\x32\xbd\x63\x1d\x7e\xc3\x36\xa1\x73\xff\x19\x14\x1e\x36\x4f\x78\xc7\xc4\xf4\xef\x80\x70\xd3\xfb\x10\x6e\x5c\x0f\xdd\x65\x73\xe1\xdc\xf2\x89\xd0\xb1\x25\x0f\x6e\x7c\x90\x07\xe7\x7f\x33\x81\xea\xf0\x5b\xb8\x79\x13\x61\xd0\x8b\xa7\x2c\x09\x7c\xfc\x57\xac\x7b\x11\xf6\x7f\x6a\x5f\xd4\xb8\x06\xa1\xfa\x15\x10\x6e\x7a\x0f\x3a\xb4\xc7\xa0\x61\x55\x3e\x04\x8f\xfc\x39\x84\x3e\xfa\x4b\x68\x5c\x93\x4f\x75\xf8\x2d\x74\x71\x05\x44\x45\x13\xea\x54\xeb\x7c\x85\xa9\x48\xf8\x1e\x15\xcd\xf4\xcd\xee\x7f\x71\x65\x3e\x04\x3e\x7c\x86\x78\x5c\x5a\x1d\xd3\xbf\x7e\x05\xa0\x2c\x94\x89\xb2\x03\xd5\x3f\x66\x88\x05\x31\x21\x36\x1b\xff\xd9\xe5\x13\xe1\xda\xc6\x5c\x68\xdf\x94\x4b\x63\x71\xf0\x37\xbd\x0f\x38\x56\x1c\x33\x8e\x1d\x75\x80\xba\x40\x9d\x0c\xd6\x5f\xcd\x92\x29\x44\x83\xf5\x87\xba\x76\xfa\x73\x85\xe1\x5c\x18\x07\x9e\x38\x86\x18\x68\x8e\xac\xf9\xbb\xbe\x39\x87\xc8\x99\xbf\x13\x3f\xa3\x39\xc6\xb9\x8e\xed\x4f\xb6\xe0\x61\x3f\x40\xdb\x40\x1b\x41\x5b\x19\x62\x3f\xc7\x7f\x4a\xb6\x85\x0f\xda\x9a\xdd\x9f\xd6\x80\xe6\x62\xd2\x26\xd5\xd7\xd0\x46\xc9\x56\x0f\x3c\xde\x6f\xbf\xe5\xb3\xc8\xa6\xd1\xb6\xc9\xc6\x4f\xbf\x01\x68\xf3\x68\xfb\xce\x3a\xf2\x30\x26\x76\x32\x85\xd6\x08\xae\x95\xc1\xeb\x87\xd6\x14\xb3\x69\x11\xad\x39\xaf\x5b\x1d\xed\xfa\x05\x60\x8c\x65\x32\xc6\x54\x8b\x5c\x31\x64\x3d\x0b\x63\xe8\xb0\x45\x2d\x56\xdf\x99\x96\x8f\x99\x1b\xeb\x67\xf2\x47\x8b\xea\xf3\xf9\x08\xae\x22\xe5\x09\xae\x4e\x17\x5c\xfd\xc2\x28\x69\x8a\xe0\xca\x58\xb4\x3d\xdb\x17\xa6\x29\xff\x5f\x04\x57\x5b\x85\xe6\xba\x92\x90\xb8\x1a\x43\x4a\x4c\xbd\x62\xd7\xb7\x08\xae\xd6\x0b\xae\xfe\x8f\xe0\xea\x9b\x38\x1e\x1f\x1f\x4b\xfe\x34\xb5\x7c\xa5\x40\xec\xca\x02\xbd\x64\x3a\xe8\xfb\xbe\x38\x94\x4a\xa6\x83\xf0\x66\x82\xd0\x14\x10\x9a\x0b\x44\xe1\x38\x5c\x2b\x44\xf8\x4e\x75\x5c\x01\xe1\x75\x03\xf2\x11\x9a\x2b\x2a\xb8\x52\x2b\xb8\x3a\x4f\x68\x8a\x92\x0c\x03\xc9\xf7\xb0\x02\xa3\xfc\x2b\x10\xe9\xae\x85\xa8\x7e\x15\xa2\xfa\x95\x18\xba\x0a\xa1\xb3\x8b\x49\xbe\xbe\xe7\x5e\x08\xfe\xef\x3f\x81\xd9\x51\x09\x91\xbe\x4b\x44\xf8\x8e\x75\xf8\x4d\x70\x37\x04\x3e\xfe\x11\xf9\x14\xbd\x68\x3c\xe2\xe8\x14\x5c\x7d\x49\x78\x99\x2b\x11\x06\x47\xfe\x81\xd9\x10\x0d\xde\x82\xc1\x8f\x79\xe3\x30\xe8\x7b\xa7\x81\x51\xfa\x28\x98\xd7\xca\x00\xa2\xa6\xfc\x80\x65\xcc\x3b\x7e\xc3\x36\xfa\xde\x07\x08\x93\xd9\x5a\x0c\xfa\xfe\x87\x41\x68\xac\x43\x70\xf5\x59\x92\xe3\x1d\xea\x1a\x92\xc9\x8f\x06\x3a\xc1\xff\xe1\x5c\xda\x67\x91\x27\x3e\x91\xde\x0b\xe4\xaf\xfc\x47\x5f\x20\xc2\x77\xac\x23\xac\x1d\x95\xd4\xd6\xff\xe1\x9f\x10\x2f\xfa\x5d\xf2\x05\xc4\x70\x4c\x70\xf5\x7e\x94\x35\x1c\xf9\xa1\xc6\x77\x68\x3e\x43\x17\x57\x4a\xfe\xe8\xc3\xcb\xbe\x8c\xfc\xa0\xcf\x23\x09\xdf\xb1\x0e\xbf\x51\x9f\x8b\x2b\x65\x9f\xc6\x77\xe8\x37\xee\x4f\x62\xd7\x58\xb4\x8f\x05\x3e\x9e\x35\xc4\x1e\x13\xc9\x8f\x06\xbb\xc0\xa8\xfc\x23\xf0\x1f\xfa\x63\x88\x86\xba\x21\x72\xeb\x24\x18\xfb\x1f\x82\xde\x1d\x2e\x68\xdd\x30\x0e\x1a\x56\x4f\x24\xc2\x77\xac\xc3\x6f\xd8\x06\xdb\x62\x1f\xec\x8b\xef\x10\xd6\xc1\xff\xd1\xf7\x11\x67\x83\xe0\xea\x97\x06\xeb\x20\x91\x7c\xd2\xdd\xee\x3c\x08\x37\x6f\x04\x30\x0d\x08\x7c\x34\x8f\xe4\x34\xae\x99\x00\xa7\x7f\x3d\x0d\x4e\xfd\xea\x7e\x22\x7c\xc7\x3a\xfc\x86\x6d\xb0\x6d\xb8\x69\x23\xf5\xb5\xe7\xcc\x6c\x3f\x00\x62\x77\x6e\x54\x68\xae\x57\xad\xf5\x96\x52\x7e\xb0\x6e\x01\xe8\xfb\x66\xd0\x1a\x30\xaf\x95\x83\x28\xcc\x81\x2b\xef\xe6\x92\x3c\x24\x8a\x63\x96\x4d\x71\x7e\xe3\x37\x6c\x83\x6d\xb1\x0f\xf6\x0d\xd6\x15\x48\x5d\x86\x7c\x64\x47\xc2\xc3\xf6\x0a\xae\x66\xc6\xea\x20\xae\xfc\x48\x08\xfc\x55\xdf\x05\x7f\xd5\x77\x00\x22\x41\x08\x7e\xfa\x2a\x74\x6d\x53\xa1\x6e\xf9\x64\x92\x75\x69\xcd\x44\xb8\xb9\x35\x13\x6e\x6d\xcb\x80\xe6\x75\xe3\xa9\x0e\xbf\x61\x1b\x6c\x8b\x7d\xb0\xaf\xbf\xea\x7b\xc4\x8b\xec\xe2\xec\x22\x9c\x83\x66\xc1\xd5\x19\xa9\xe4\xd3\xdc\x1f\x98\x0d\xc1\x33\x6f\x42\x34\xec\x03\x7f\xe5\x1c\x68\xdb\x30\x96\xe4\x9c\xfb\xcd\x7d\xd0\xbd\x63\x0c\xe8\x85\x63\x41\x2f\xbc\x07\x7a\x3d\x6e\xa8\x5f\x39\x89\xbe\x61\x1b\x6c\x8b\x7d\xb0\xaf\xe4\xd9\x65\xd9\x6e\x19\x88\x5d\xd9\xba\xd0\x5c\x73\x53\xca\xd7\xff\x0f\xf4\xfd\x0f\x41\xa8\x61\x35\xf9\x1f\x7d\xdf\x74\x68\x5e\x97\x47\xf3\xdd\xf2\x4e\x1e\xc9\x0d\x9f\xfc\x3b\x30\x4f\xbd\x02\xc6\x9e\x09\xd0\xba\x21\x07\x4e\xfd\x6a\x1a\xb5\xc1\xb6\xe4\xb3\x1a\x56\x13\x0f\xe4\x25\xd7\xed\x39\x5c\x9b\x51\xa1\xb1\x17\xe3\xc8\x7f\x0b\x63\x13\x5c\xef\xd4\xb6\xaf\x91\x7c\x6b\xf8\xf2\x16\x5a\xdb\x62\xcf\x64\xb8\xb4\x7a\x3c\xc9\x68\xdb\x90\x0d\xfe\xb2\x87\x01\x2e\x2d\x07\x68\x5a\x05\x81\xca\x27\xe1\xfa\xe6\x4c\x39\x2f\xab\xc7\x53\xdb\x88\xaf\x9e\xfa\x22\x0f\xe4\xe5\xc4\x47\xa5\x8f\xa2\x0d\xfc\x22\x8e\xfc\x5f\xe0\x37\x3b\x5e\x4f\x2e\xff\x1e\xf0\x97\x7e\x11\xa2\x0d\x4b\x00\x9a\x56\x42\xe0\xe0\x13\x70\x7d\x53\x56\x6a\xf9\x81\x4e\x8a\xbf\x70\xac\x43\xe4\x6b\xec\x45\xd4\x0d\xea\x28\xbe\xfe\x67\x38\xfa\x6f\x5e\x97\x2f\xfd\xcb\xd1\xe7\x21\x74\xec\x87\xa0\xef\xce\x81\xab\xbf\x1d\x67\x7d\xcb\xb3\xd6\xcc\x50\xfd\xe3\xdc\xe2\x1c\xcb\xb3\xd0\x60\xf9\xae\xb9\x68\x1b\xe4\xdf\x53\xd8\x1f\xda\x39\xda\xbe\xee\x55\x41\xf7\xba\xc9\x16\xcf\xaf\xb8\x2f\xa5\xfd\x25\x95\x8f\x6b\x42\x63\xcd\xb8\xcf\x25\x5c\x7f\x5b\xe5\xfa\xc3\x39\xb8\xf0\xdf\xf7\x42\xfb\xc6\x7b\xa0\x63\x53\x36\x5c\x5c\x35\x29\xad\xf5\x97\x42\x7e\x26\xfa\x06\xda\x37\xc2\xbe\xb4\xfc\x0f\xd2\x99\x25\xd3\xd2\xf2\x3f\xc9\xe5\x2b\xf6\x1c\xbc\x86\x3e\x12\x7d\xe5\x50\xff\xeb\x8f\xe3\x7f\x25\x0d\xf5\xbf\xfe\x21\xfe\x37\x99\xfc\x18\x1d\x7c\x09\xf7\x08\xdc\x2b\xc0\xd4\x69\xef\x30\x2a\x46\xb9\xff\x58\x73\x9f\x5a\xbe\xc2\x7c\x1a\xed\x8d\x0b\x70\xaf\xc4\x3d\x53\xee\xbf\xef\x8e\x7a\xff\x4d\x47\x7e\x8c\x0e\xee\xc7\x58\x01\x63\x06\xd4\x1d\xf6\x41\x9b\x18\x51\xfc\x61\xf9\xb2\xb4\xe5\x7b\x55\x1b\xc3\xb3\x18\x33\x61\xec\x84\x31\x14\xf1\xdc\xfb\x40\x5a\xf1\x97\x4e\xf1\xd7\x34\x8a\xd9\x06\x3f\xa9\xe4\x3b\xb6\x48\xb1\xa2\xfa\x12\xc6\x8e\x18\x43\x62\x2c\x89\x31\x25\xc6\x96\x29\xe3\x4f\x6f\x26\xc5\xaa\xf1\x62\x58\x8c\x6d\x31\xc6\x4d\x26\xbf\x7f\x3d\x60\xcc\xac\xce\xa3\x18\x1a\x63\x69\x8c\xa9\xb9\x5b\xc6\xd8\x14\x7f\xe7\xc4\xc4\xdf\x39\xb2\x0e\x63\x73\x8c\x91\x93\xc5\xf0\xc4\x47\x49\x2a\x9f\x30\x68\x0a\xf3\x15\x65\x31\x6b\xaf\x7e\xd3\x3a\x53\xd4\x5b\x67\x8c\x44\xe7\x0f\x49\xc9\xcf\x30\xad\xd6\x59\x27\xa9\xfc\x58\x5d\xf4\x69\x14\x2f\x8d\x95\x67\xab\x51\x9f\xcf\xa6\x5b\x67\xbd\xb4\xe4\x7f\xde\x1f\x7b\x6d\x1c\x66\x2a\x1c\x66\x0c\xe9\x99\xc3\x8c\x4d\xb7\x28\xcf\xa2\x4c\x8b\xd4\x74\xa8\xc5\xa2\x1e\x8b\x02\x16\x99\x8c\xa9\xc0\x48\x90\x6a\xcb\x9d\xc9\x18\x9b\xcd\x18\xfb\xfb\xd8\x3c\xc5\xc3\x9f\xb5\x56\xee\x3e\x77\x9f\x3f\xcc\xc7\xda\x1f\x1f\x16\x5c\x7d\x5b\x70\x75\xa1\xe0\x6a\xc1\xef\x89\x6c\xde\xff\x2a\xb8\xfa\x37\x82\x2b\xb3\x05\x57\x72\x7a\x35\xc6\x0c\x2d\x79\x3e\x29\x0d\xfc\xdf\x16\x5c\x0d\x0a\xae\xc2\x1d\x22\x53\x70\xb5\x53\x70\xf5\x20\xed\xeb\x5c\xc9\xc7\x58\xc3\x48\x33\x3f\x97\x18\xbf\x42\xb1\x57\x5a\x84\x6d\x07\x60\x4a\xd2\x37\x6e\x5b\x97\xfd\x1b\xf5\x56\x2a\xb8\xfa\x24\xec\x67\x4c\xe7\xc3\x1b\x83\x83\x5f\x73\x05\x31\xc6\x0b\xd6\xbe\x45\xe7\xf2\x64\x14\x3c\xbb\x08\x8c\x8a\xa7\xfa\x71\x21\xc6\xc2\x1c\x30\x0e\x7e\x0d\x02\x27\xfe\x81\x78\x20\xe1\x3b\xd6\xc9\x78\x84\x39\xd8\xf5\xe2\x29\x60\x94\xcd\x04\xe1\xcd\x8a\x1d\x47\xa3\xe0\xea\x0b\x7e\xce\x5c\x7a\x8a\xfc\x64\x7c\xfc\x2c\x48\xf1\x75\xb8\x6f\x48\x9c\x37\xf8\x31\xdb\x4a\x64\x9c\x84\xb2\xbd\x19\xe0\x3f\xf2\x17\x60\xb6\xee\x85\x68\xe0\x26\x40\x34\x12\x13\x20\x46\xa8\x0e\xbf\x61\x1b\x6c\x8b\x7d\xf4\xe2\xc9\x10\x6a\x58\x0b\xe1\xcb\x5b\xc1\xa8\xfc\x46\xff\x9c\x70\xb5\x0d\xc7\xd0\xa3\x65\x33\x91\xe6\x3c\x0c\x17\x7f\xa4\xeb\x14\xc5\xb4\xc2\xc3\xe8\x3c\x43\xb1\x65\xb0\x3b\xe5\x98\xb1\x0d\xb6\xc5\x3e\xf2\x7c\x30\x13\x22\xdd\x35\x10\xf5\x5f\xa7\x73\xa9\x28\xbc\xc7\x1e\x43\x93\xe0\xca\x53\x14\x73\x16\xa6\x8e\x89\x86\x83\x9f\xce\x41\x55\xdf\x23\xec\x62\x77\x2e\x9d\xc7\xed\xb3\xa8\x1c\x5c\x10\xa2\xa2\x05\x22\xb7\x3e\x21\xc2\x77\xac\xeb\xff\x1e\xa2\x3e\xd8\x17\x79\x20\xaf\x68\xa8\x87\xda\x84\xea\x97\xcb\xbc\xb3\x1c\x43\xa5\xe0\xea\xe4\x74\x62\xba\xe1\xe0\x0f\x35\xac\x91\x36\xc0\xdd\x10\xac\x7b\xbb\x1f\x7b\x34\x0c\x66\x47\x05\xdd\x3d\xe9\xa5\x8f\x80\x5e\x34\x51\x52\xe9\x23\xf2\x3e\xaa\xa3\x82\xda\xd8\x63\x08\xd6\xfe\x52\xc6\xee\xde\x0c\xe2\x69\x8f\x1d\xcf\x4d\x92\xbf\x1a\x45\x7f\xeb\xf3\x32\x57\x5f\x0a\x3b\x4a\x17\x3f\xea\xd2\x28\x9f\x25\xf5\x76\xe4\x3b\xfd\x79\xad\x70\x1f\x84\xce\xff\x17\xe8\x7b\x26\x59\xfe\xc6\xca\xdb\xdb\x3e\x46\x63\xf4\x0d\xdb\xd8\xbc\xe9\x0c\x78\xe4\x79\xe2\x85\x3c\x69\x9e\x68\x7e\x7b\x21\x50\xfd\x63\x7b\x4d\x5f\x13\x5c\xfd\x7a\xca\x73\x45\x9a\xf8\x69\x7e\xad\xb5\x67\xde\xa8\xea\xd7\x59\xdd\x02\x79\x0f\xc1\xa5\x1f\xe9\xd9\x39\x06\x6e\x6e\xcd\x22\xc2\x77\xb9\x36\x5d\xd4\x06\xdb\xda\xf6\x64\xde\x38\x42\xbc\x90\x27\xf2\x76\x4c\xac\xbb\x86\x72\x44\xd6\x18\xde\xd3\xb9\xcb\x9d\xcc\x1f\xa5\x83\x9f\xf2\x5c\x15\x73\x48\x5f\x81\x4f\x5f\x75\xce\xa8\xe1\xcb\x5b\x1c\x9b\x45\xac\x57\xde\xcd\xa3\x5c\x66\xcd\xd2\x29\x44\xf8\x8e\x75\x72\x1c\x0a\xb5\xc5\x3e\x92\xa9\x49\xbc\x68\x0e\x2a\xe6\x0c\x38\x5b\x87\x2e\x2c\xb1\xce\x86\x6a\xbb\xe0\xea\x57\x93\x9f\x2d\x53\xe3\xa7\x5c\xcf\xae\x6c\xb2\x03\xb3\xb3\x5a\xea\xa9\xaf\x51\x9e\x5d\x35\x06\xdd\xdb\x33\x9c\x9c\x53\x3c\xc2\x6f\xd8\x86\xfc\x4e\xf9\x57\x9c\xfc\x9f\xd9\xf9\xb1\xb4\xbb\xc2\x6c\x30\xdb\xcb\xfb\xf5\xa5\x5f\x95\x79\x41\xb9\x67\x2c\x12\xdc\x9d\xf0\xce\x2a\x1d\xfc\xe4\xdf\xc8\x5f\x7c\x97\x72\xf1\x54\x57\xf3\xef\x64\x17\xbd\x1e\x37\xe5\x6c\x12\x61\xb7\x09\xdb\x60\x5b\xec\x83\x7d\xe5\x00\x0c\xe2\x89\xbc\x51\xc6\x00\x99\x75\x0b\x6d\x1b\x3a\x2e\xb8\x32\x61\xa4\xf8\xf1\x37\xe5\xe9\x35\x06\xa1\x86\x55\x96\x7e\x5a\xc0\x28\x9d\x49\x75\xad\xef\x8d\x1b\x82\xb5\x76\xd9\xc0\x7b\x00\x9b\xb0\xad\xed\xfb\xa3\xfa\x15\x69\x2b\x17\x57\x51\x1d\xca\x88\x95\x8d\xfe\x57\xde\x9f\x29\x3e\xc1\xd5\x6f\x26\xce\x8f\x24\xc7\x1f\xf1\x35\x80\x5e\xf2\x00\xe8\xbb\xf3\x21\x72\xf3\x98\x65\xf7\x1f\x80\xf0\x8e\x81\x9e\x1d\x63\xc8\xc6\x6d\x7c\x35\x4b\xa7\x92\xbd\xdf\xda\x96\x09\x5d\xdb\x33\xa1\x6d\x43\x8e\x73\xbf\x60\xe7\xf9\xb1\x0f\xfa\x48\xdc\x7b\x89\xff\xcd\x6a\xe2\x4d\x79\x65\x5f\xc3\x40\xbd\xfd\xee\x5b\xb6\x0d\x51\x7e\x3b\xde\x3a\x4e\x85\xdf\xec\x38\x44\xb6\x6f\x94\x3d\x46\xff\x67\xc0\x27\x70\xf2\x65\xe2\x7b\xed\xfd\x9c\x98\xbc\xe7\x54\xa9\x5f\x27\x16\x50\x40\xe7\x2a\x5c\xdf\x9c\x4d\xf7\x32\x76\x3b\xec\x83\x7d\x03\x9f\xfc\xa3\xc4\xe9\x6f\x27\xde\x28\x03\x65\x0d\xb0\xa1\x9a\xff\xb0\xf1\x6f\xd3\xb9\x2b\xee\x9d\x69\x2a\xfc\xa4\x6b\x9c\xdf\xc3\xcf\x01\x44\x02\x94\xcb\x35\x2a\x9f\xa6\xba\xa6\xb5\x13\x9c\x7c\x29\xae\x51\x9f\x47\xe6\x93\xf4\xc2\x6c\xd0\x77\x8f\x73\xe2\xcd\xcb\xeb\xf3\x9d\xfc\x2a\xf6\x21\x7e\x87\x9e\x96\x79\xe1\x48\x80\x78\x63\x1d\xca\x1a\x20\xfb\x2a\xb7\xf7\xb3\x6a\xc1\x95\xdc\x91\xe0\x0f\x5d\x58\x26\xfd\xe6\xf1\x97\xa4\xbe\xc4\x65\xca\x5d\xf5\xee\x54\x29\xe7\x6d\xeb\xb5\x0d\xf5\xca\x5d\x60\xec\x9d\x4a\x77\x30\x91\xda\x37\xc0\x5f\x3e\x93\xc6\xd3\xf9\xc1\x58\xb2\x2d\x3b\x4f\xee\xdb\xa9\x12\x0f\xe4\x45\xf3\x79\xfc\x25\x92\x81\xb2\x06\xd8\xee\xcd\x13\xf2\xce\x98\x2b\x4d\x82\xab\x0f\x26\xc9\x91\x3e\x97\x08\x3f\xed\x4f\x3b\x19\x04\x4f\xbd\x2e\x79\xf6\xd4\xd2\xbe\xd3\xbd\x7d\x0c\x9c\xb5\x6c\x1b\xb1\xe1\x7e\xa5\x7b\x55\x08\x1d\xfb\x11\x40\xf3\x5a\x22\xf3\xf4\xcf\x69\x2e\xd0\xff\xa3\xed\x23\x7e\xec\xd3\xbd\xdd\x4d\x31\x74\xa4\xa7\x4e\xca\x38\xf5\xba\x94\x81\xfb\x5b\xac\xef\x10\xcd\x74\xf7\x64\xdd\x79\xcf\x4a\x82\xff\x5b\x42\x63\x86\x71\xf0\xc9\x21\xb1\x24\xf9\x4e\xe4\x5d\x3b\xdf\xd2\xc9\x71\xd0\x8b\xf2\xa1\x6b\x5b\x86\xe3\x63\xb0\xec\xda\x9e\x41\xf7\x6f\x66\xcd\xeb\x00\xcd\x6b\x00\x9a\x56\x43\xb4\x7e\x31\x18\x7b\x27\x83\xcf\x23\xe7\x0a\xf1\x3b\x6d\x8b\xf2\xe9\xff\x3a\x24\xa3\x76\xbe\x94\x31\xc8\x87\x62\x6c\x4a\x31\x8b\xc6\xfa\x04\x57\x9f\x4e\x82\xff\x69\x6c\x43\xb1\x88\xff\xfa\xc8\xf0\x6f\xcb\x00\x7d\x57\x16\x98\x67\x7e\xde\x8f\xff\x42\x01\x18\xc5\x93\x08\xff\xf9\x91\xe0\x0f\x76\x03\xea\x14\x75\x4b\x3a\x4e\x8c\x7f\x16\xe5\x96\xf7\xcd\xa0\x39\x4b\x6e\x3f\x75\x34\xf7\x68\x03\xb6\xfd\xa0\xef\xb9\xb1\x85\xee\xd3\x21\x78\xe4\xcf\xe8\x0e\x10\xed\x27\x7c\xe2\x45\xd0\xbd\x19\xd0\xbd\x23\x83\xda\x0e\xdb\x7e\xc2\x7d\x74\x67\x81\xb6\x4d\x36\x9e\x18\xff\x83\xb8\x46\xf4\xa2\x09\x74\xdf\x31\x92\xf5\x7b\xf5\xb7\xb9\x96\xef\xc9\x81\x60\xd5\x73\x10\xaa\xfe\x3e\xe8\xc5\xf7\x82\xce\x15\xba\xd3\xc2\x31\x9e\xfa\xb5\xbd\x7e\x95\xb4\xd6\xef\x20\xfc\xdf\x8e\x8f\x9f\x72\xeb\xb9\xe4\xa3\x70\x5f\xb9\xca\x07\xf0\x48\xd7\x7f\x9e\x5b\x81\x7b\x53\x46\x7f\xec\x6c\x91\x4f\xeb\x8f\x2f\xb0\x6d\xe3\x30\xfc\x67\xba\xf8\xe5\xde\xa0\x6e\x47\x1e\xc1\x9a\xf9\x03\x78\xa4\xbb\x7f\x21\x35\xad\x1b\x0f\xbd\x9e\x31\xd6\x3d\xa6\x0a\x7d\x9a\x9b\xe6\x05\x75\x3f\x64\xff\x3a\xf9\xb2\xb5\x46\x13\xef\x5f\xe9\xe0\xd7\xed\xbb\x3e\xdc\xa3\x51\x2f\xbf\xfb\xd3\x81\x71\x48\xdc\xf8\x61\x6b\xdc\xf8\xc1\xde\xc7\x30\x6e\x40\x9c\xa8\xeb\x58\xec\xc3\x89\x1f\xd2\xc5\x1f\xb3\x06\xbe\x29\xb8\xd2\xa7\xef\xb9\x0f\x22\x5d\x9f\x0e\xe0\xe1\xc4\x6f\x17\xd3\x8b\xdf\x12\xd1\x70\xe2\xb7\xe1\xe1\xa7\x35\x30\x81\x62\x55\xcd\x45\xe7\xd0\xd8\xe7\xb3\x88\x9f\x87\x85\x5f\x53\x18\xfd\x3f\x95\xab\x8b\xe5\x19\xe3\x71\x3a\x43\x38\x6b\xe0\xf7\x76\x7e\xa9\x8e\x7b\x7e\x19\x2e\xfe\x18\x1b\xc2\xb3\x5a\x07\x9e\xdd\xf0\x0c\xe7\xf0\xb9\x23\xe7\xc7\x9b\xa3\xc4\x4f\x7e\xc8\x8d\x67\x66\x3a\xa7\xef\x7f\x88\xce\xd2\xf6\x93\xf6\xf9\x7d\x47\xcc\xf9\x7d\x47\xb2\xf3\x7b\x55\xdc\xf3\xfb\x48\xf1\xc7\xcc\xc1\xd7\x28\x77\x81\x3e\xae\xfa\xaf\x29\xa7\x01\x43\xf2\x27\xcf\x0f\xcc\x9f\x9c\xbb\x3d\xf9\x93\xd1\xe2\xef\xe3\x2e\xe6\xe3\x4c\x91\x67\x66\x35\x8a\x7e\x8e\xd6\xb2\xa5\xb3\xd4\xf9\xab\x17\xc1\x28\x7d\x04\xf4\x3d\x13\x89\x8c\x44\xf9\xab\xba\xb7\x87\xe6\xaf\x6e\x03\xfe\x98\x39\x98\x2c\xb8\x7a\xc8\xb6\xd9\x50\xfd\x32\x99\x1b\x0c\xf5\x38\xfe\xe2\xf6\xe5\x0f\x13\xe7\x4e\x47\x84\xbf\x50\xb5\xfd\xe9\x1c\x2b\x97\x4a\xb9\x55\xfa\xdf\x50\xe0\x3a\x44\xba\xcf\xc8\xdc\x37\xda\x8a\x9d\xbf\x4d\x82\xc1\xc1\x62\xe5\x6f\xb1\x0f\xe5\x7e\x4b\x1f\xa5\x5c\x70\xd2\x3e\x23\xc0\x4f\x63\xf0\xba\x58\x8f\x27\x1b\xe7\xe1\x05\x99\xd3\x56\x68\xbe\x8d\xca\x6f\xd0\xbe\x19\x6a\x58\xeb\xac\x3d\x27\x7f\xde\x56\x92\x7e\xfe\xbc\xe4\x41\x30\xdb\xf6\xa5\x1e\xf3\x08\xf1\xe3\xa3\x7b\x15\x66\xec\xa2\xff\x6d\xbc\x60\xdd\x2d\x48\xbc\xbb\xb2\xe8\xbf\x31\x7a\xf1\xd4\x24\xf7\x17\xf3\x89\x12\xdd\x5f\x18\x15\x4f\xd1\x1d\x48\xca\x7b\x92\xda\xb7\xac\xff\xda\xba\x86\x8d\x9f\xc6\xc0\x5d\x0c\xbc\xe4\x5b\xd1\x27\x95\x39\x77\x64\x8e\x8f\x19\xcd\xfd\xd1\xb0\xee\xa9\x46\x84\x1f\x1f\x43\x73\xd9\x6b\x7a\xbc\xbc\x6b\xa3\x3b\xb7\x4e\xeb\x0e\xee\x4e\xdd\xf7\x8d\x18\xbf\x33\x0e\x8f\xc2\x7a\x8b\x54\x8c\x35\x72\x04\x57\x67\x0b\xae\xfe\xc4\xba\x0b\x2d\xb8\x03\xf7\xae\x6f\x5b\x77\xbc\x23\xc6\x7f\xf7\xb9\xfb\x7c\x9e\x1e\xb9\x43\x24\x2e\x5b\x18\x63\xcf\x58\x65\x9e\x55\x66\x5a\xa5\x6b\x50\xc9\xec\xb2\xc0\x2a\x9f\x19\x54\x4e\x4f\x50\xe6\x25\x28\x33\x6f\x5f\xd9\x93\xa0\x0c\x24\x28\xcd\x41\x65\xd4\x2a\xc1\x2e\x17\x0e\x2a\x5b\xac\xb2\xc7\x2a\x4d\xab\x4c\xa1\xdf\xff\x0f\x00\x00\xff\xff\xc6\xb9\x24\x2f\xee\x3a\x00\x00" - -func faviconIcoBytes() ([]byte, error) { - return bindataRead( - _faviconIco, - "favicon.ico", - ) -} - -func faviconIco() (*asset, error) { - bytes, err := faviconIcoBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "favicon.ico", size: 15086, mode: os.FileMode(438), modTime: time.Unix(1565946440, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _indexHtml = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x64\x91\x3d\x4f\xc3\x30\x10\x86\xf7\x48\xf9\x0f\xd7\x9b\x40\x22\x35\x6c\x0c\x71\x96\x42\x57\x90\x28\x03\xa3\xeb\x5c\x9b\x6b\x1d\x27\xb2\x2f\x69\xfb\xef\x51\x3e\xca\xe7\x64\xdf\xeb\xbb\xc7\x8f\xec\x7c\xf1\xf4\xb2\xda\x7c\xbc\x3e\x43\x25\xb5\x2b\xd2\x24\x1f\x56\x70\xc6\xef\x35\x92\xc7\x22\x4d\x86\x8c\x4c\x59\xa4\x09\x00\x40\x5e\x93\x18\xb0\x95\x09\x91\x44\xe3\xfb\x66\x9d\x3d\xe2\xaf\x33\x6f\x6a\xd2\xd8\x33\x9d\xda\x26\x08\x82\x6d\xbc\x90\x17\x8d\x27\x2e\xa5\xd2\x25\xf5\x6c\x29\x1b\x8b\x3b\x60\xcf\xc2\xc6\x65\xd1\x1a\x47\xfa\x61\x79\xff\xc5\x72\xec\x8f\x10\xc8\x69\x64\xdb\x78\x84\x2a\xd0\x4e\xa3\x6a\xbb\xad\x63\xab\x76\xa6\x1f\xe2\x25\xdb\x06\x41\x2e\x2d\x69\xe4\xda\xec\x49\x9d\xb3\xa9\x5d\xfd\xe7\x44\xb9\x38\x8a\x15\x91\xfc\xa5\xd9\x18\x55\x6d\xd8\x2f\x6d\x8c\x3f\x46\x85\xc5\x51\xb1\x66\x47\xf0\x46\xa1\xa7\x90\xab\x29\x4a\x93\x5c\xcd\x6f\x92\x26\xf9\xb6\x29\x2f\xd7\x11\xf6\x6d\x27\xb3\xd0\xb6\x13\x19\x54\x1a\x6f\x1d\xdb\xa3\xc6\xc6\xaf\x86\xcd\xcd\x2d\x42\x6f\x5c\x47\x1a\xc7\x1a\x6a\x5a\x4c\xb7\xce\x90\x68\x03\xb7\x57\x8a\xd0\x59\xd4\xc1\xf4\x66\x4a\x11\x62\xb0\xdf\xe6\x87\x59\xfc\x10\xb1\xc8\xd5\xd4\x32\xea\xcd\x52\xa3\xe9\xf0\xb3\x9f\x01\x00\x00\xff\xff\x00\xcf\x96\xa8\xe9\x01\x00\x00" - -func indexHtmlBytes() ([]byte, error) { - return bindataRead( - _indexHtml, - "index.html", - ) -} - -func indexHtml() (*asset, error) { - bytes, err := indexHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "index.html", size: 489, mode: os.FileMode(438), modTime: time.Unix(1594886229, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _jsMainJs = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xce\xcf\x2b\xce\xcf\x49\xd5\xcb\xc9\x4f\xd7\x50\x4a\xad\x48\xcc\x2d\xc8\x49\x55\xd2\xb4\xe6\xe5\xe2\xe5\x4a\x2b\xcd\x4b\x2e\xc9\xcc\xcf\x53\xc8\xcf\x73\xce\xc9\x4c\xce\xd6\xd0\x54\xa8\xe6\xe5\x52\x50\x50\x50\x28\xcf\xcc\x4b\xc9\x2f\xd7\x4b\xcc\x49\x2d\x2a\xd1\x50\x4a\x2a\x2d\x29\xc9\xcf\x53\x48\x06\xa9\x49\x4d\x01\x6b\xae\x05\x04\x00\x00\xff\xff\xa4\xb7\x99\x52\x57\x00\x00\x00" - -func jsMainJsBytes() ([]byte, error) { - return bindataRead( - _jsMainJs, - "js/main.js", - ) -} - -func jsMainJs() (*asset, error) { - bytes, err := jsMainJsBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "js/main.js", size: 87, mode: os.FileMode(438), modTime: time.Unix(1594787764, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "app2/app2app3/css/main.css": app2App2app3CssMainCss, - "app2/app2app3/dirs/dir1/text.txt": app2App2app3DirsDir1TextTxt, - "app2/app2app3/dirs/dir2/text.txt": app2App2app3DirsDir2TextTxt, - "app2/app2app3/dirs/text.txt": app2App2app3DirsTextTxt, - "app2/app2app3/index.html": app2App2app3IndexHtml, - "app2/index.html": app2IndexHtml, - "app2/mydir/text.txt": app2MydirTextTxt, - "css/main.css": cssMainCss, - "favicon.ico": faviconIco, - "index.html": indexHtml, - "js/main.js": jsMainJs, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"} -// AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("notexist") would return an error -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "app2": {nil, map[string]*bintree{ - "app2app3": {nil, map[string]*bintree{ - "css": {nil, map[string]*bintree{ - "main.css": {app2App2app3CssMainCss, map[string]*bintree{}}, - }}, - "dirs": {nil, map[string]*bintree{ - "dir1": {nil, map[string]*bintree{ - "text.txt": {app2App2app3DirsDir1TextTxt, map[string]*bintree{}}, - }}, - "dir2": {nil, map[string]*bintree{ - "text.txt": {app2App2app3DirsDir2TextTxt, map[string]*bintree{}}, - }}, - "text.txt": {app2App2app3DirsTextTxt, map[string]*bintree{}}, - }}, - "index.html": {app2App2app3IndexHtml, map[string]*bintree{}}, - }}, - "index.html": {app2IndexHtml, map[string]*bintree{}}, - "mydir": {nil, map[string]*bintree{ - "text.txt": {app2MydirTextTxt, map[string]*bintree{}}, - }}, - }}, - "css": {nil, map[string]*bintree{ - "main.css": {cssMainCss, map[string]*bintree{}}, - }}, - "favicon.ico": {faviconIco, map[string]*bintree{}}, - "index.html": {indexHtml, map[string]*bintree{}}, - "js": {nil, map[string]*bintree{ - "main.js": {jsMainJs, map[string]*bintree{}}, - }}, -}} - -// RestoreAsset restores an asset under the given directory -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil -} - -// RestoreAssets restores an asset under the given directory recursively -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) -} diff --git a/_examples/file-server/http2push-embedded/main.go b/_examples/file-server/http2push-embedded/main.go deleted file mode 100644 index a29283f240..0000000000 --- a/_examples/file-server/http2push-embedded/main.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "regexp" - - "github.com/kataras/iris/v12" -) - -// How to run: -// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata -// $ go-bindata -nomemcopy -fs -prefix "../http2push/assets" ../http2push/assets/... -// # OR if the ./assets directory was inside this example foder: -// # go-bindata -nomemcopy -refix "assets" ./assets/... -// -// $ go run . -// Physical files are not used, you can delete the "assets" folder and run the example. - -var opts = iris.DirOptions{ - IndexName: "index.html", - PushTargetsRegexp: map[string]*regexp.Regexp{ - "/": iris.MatchCommonAssets, - "/app2/app2app3": iris.MatchCommonAssets, - }, - Compress: false, - ShowList: true, -} - -func main() { - app := iris.New() - app.HandleDir("/public", AssetFile(), opts) - - // https://127.0.0.1/public - // https://127.0.0.1/public/app2 - // https://127.0.0.1/public/app2/app2app3 - // https://127.0.0.1/public/app2/app2app3/dirs - app.Run(iris.TLS(":443", "../http2push/mycert.crt", "../http2push/mykey.key")) -} diff --git a/_examples/file-server/http2push/assets/app2/app2app3/css/main.css b/_examples/file-server/http2push/assets/app2/app2app3/css/main.css deleted file mode 100644 index d60afbdf58..0000000000 --- a/_examples/file-server/http2push/assets/app2/app2app3/css/main.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: blue; -} \ No newline at end of file diff --git a/_examples/file-server/http2push/assets/app2/app2app3/dirs/dir1/text.txt b/_examples/file-server/http2push/assets/app2/app2app3/dirs/dir1/text.txt deleted file mode 100644 index fc7b9c6439..0000000000 --- a/_examples/file-server/http2push/assets/app2/app2app3/dirs/dir1/text.txt +++ /dev/null @@ -1 +0,0 @@ -app2/app2app3/dirs/dir1/text.txt \ No newline at end of file diff --git a/_examples/file-server/http2push/assets/app2/app2app3/dirs/dir2/text.txt b/_examples/file-server/http2push/assets/app2/app2app3/dirs/dir2/text.txt deleted file mode 100644 index 9fb3e0477a..0000000000 --- a/_examples/file-server/http2push/assets/app2/app2app3/dirs/dir2/text.txt +++ /dev/null @@ -1 +0,0 @@ -app2/app2app3/dirs/dir2/text.txt \ No newline at end of file diff --git a/_examples/file-server/http2push/assets/app2/app2app3/dirs/text.txt b/_examples/file-server/http2push/assets/app2/app2app3/dirs/text.txt deleted file mode 100644 index eaad512499..0000000000 --- a/_examples/file-server/http2push/assets/app2/app2app3/dirs/text.txt +++ /dev/null @@ -1 +0,0 @@ -app2/app2app3/dirs/text.txt \ No newline at end of file diff --git a/_examples/file-server/http2push/assets/app2/app2app3/index.html b/_examples/file-server/http2push/assets/app2/app2app3/index.html deleted file mode 100644 index ba5282d220..0000000000 --- a/_examples/file-server/http2push/assets/app2/app2app3/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - App2App3 - - - -

Hello App2App3 index

- - - \ No newline at end of file diff --git a/_examples/file-server/http2push/assets/app2/index.html b/_examples/file-server/http2push/assets/app2/index.html deleted file mode 100644 index 8193d82245..0000000000 --- a/_examples/file-server/http2push/assets/app2/index.html +++ /dev/null @@ -1 +0,0 @@ -

Hello App2 index

\ No newline at end of file diff --git a/_examples/file-server/http2push/assets/app2/mydir/text.txt b/_examples/file-server/http2push/assets/app2/mydir/text.txt deleted file mode 100644 index f1cc1278cf..0000000000 --- a/_examples/file-server/http2push/assets/app2/mydir/text.txt +++ /dev/null @@ -1 +0,0 @@ -just a text. \ No newline at end of file diff --git a/_examples/file-server/http2push/assets/css/main.css b/_examples/file-server/http2push/assets/css/main.css deleted file mode 100644 index 7db3df1d78..0000000000 --- a/_examples/file-server/http2push/assets/css/main.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: black; -} diff --git a/_examples/file-server/http2push/assets/favicon.ico b/_examples/file-server/http2push/assets/favicon.ico deleted file mode 100644 index c370da518e..0000000000 Binary files a/_examples/file-server/http2push/assets/favicon.ico and /dev/null differ diff --git a/_examples/file-server/http2push/assets/index.html b/_examples/file-server/http2push/assets/index.html deleted file mode 100644 index 6e445135c1..0000000000 --- a/_examples/file-server/http2push/assets/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - File Server - - - - - - - - - \ No newline at end of file diff --git a/_examples/file-server/http2push/assets/js/main.js b/_examples/file-server/http2push/assets/js/main.js deleted file mode 100644 index f97190ca62..0000000000 --- a/_examples/file-server/http2push/assets/js/main.js +++ /dev/null @@ -1,5 +0,0 @@ -console.log("example"); - -function onClick() { - window.alert("button clicked"); -} \ No newline at end of file diff --git a/_examples/file-server/http2push/main.go b/_examples/file-server/http2push/main.go deleted file mode 100644 index 277c68f0cf..0000000000 --- a/_examples/file-server/http2push/main.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "regexp" - - "github.com/kataras/iris/v12" -) - -var opts = iris.DirOptions{ - IndexName: "index.html", - // Optionally register files (map's values) to be served - // when a specific path (map's key WITHOUT prefix) is requested - // is fired before client asks (HTTP/2 Push). - // E.g. "/" (which serves the `IndexName` if not empty). - // - // Note: Requires running server under TLS, - // that's why we use `iris.TLS` below. - // PushTargets: map[string][]string{ - // "/": { // Relative path without prefix. - // "favicon.ico", - // "js/main.js", - // "css/main.css", - // // ^ Relative to the index, if need absolute ones start with a slash ('/'). - // }, - // }, - // OR use regexp: - PushTargetsRegexp: map[string]*regexp.Regexp{ - // Match all js, css and ico files - // from all files (recursively). - // "/": regexp.MustCompile("((.*).js|(.*).css|(.*).ico)$"), - // OR: - "/": iris.MatchCommonAssets, - "/app2/app2app3": iris.MatchCommonAssets, - }, - Compress: true, - ShowList: true, -} - -func main() { - app := iris.New() - app.HandleDir("/public", iris.Dir("./assets"), opts) - - // Open your browser's Network tools, - // navigate to https://127.0.0.1/public. - // you should see `Initiator` tab: "Push / public". - // - // https://127.0.0.1/public - // https://127.0.0.1/public/app2 - // https://127.0.0.1/public/app2/app2app3 - // https://127.0.0.1/public/app2/app2app3/dirs - app.Run(iris.TLS(":443", "mycert.crt", "mykey.key")) -} diff --git a/_examples/file-server/http2push/mycert.crt b/_examples/file-server/http2push/mycert.crt deleted file mode 100644 index 9db93b09df..0000000000 --- a/_examples/file-server/http2push/mycert.crt +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFazCCA1OgAwIBAgIUfwMd9auWixp19UnXOmyxJ9Jkv7IwDQYJKoZIhvcNAQEL -BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM -GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA2MjUwOTUxNDdaFw0yMTA2 -MjUwOTUxNDdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw -HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQDlVGyGAQ9uyfNbwZyrtYOSjLpxf5NpNToh2OzU7gy2 -OexBji5lmWBQ3oYDG+FjAkbHORPzOMNpeMwje+IjGZBw8x6E+8WoGdSzbrEZ6pUV -wKJGKEuDlx6g6HEmtv3ZwgGe20gvPjjW+oCO888dwK/mbIHrHTq4nO3o0gAdAJwu -amn9BlHU5O4RW7BQ4tLF+j/fBCACWRG1NHXA0AT8eg544GyCdyteAH11oCDsHS8/ -DAPsM6t+tZrMCIt9+9dzPdVoOmQNaMMrcz8eJohddRTK6zHe9ixZTt/soayOF7OS -QQeekbr3HPYhD450zRVplLMHx7wnph/+O+Po6bqDnUzdnkqAAwwymQapHMuHXZKN -rhdfKau3rVo1GeXLIRgeWLUoxFSm4TYshrgt+0AidLRH+dCY7MS9Ngga/sAK3vID -gSF75mFgOhY+q7nvY9Ecao6TnoNNRY29hUat4y0VwSyysUy887vHr6lMK5CrAT/l -Ch8fuu20HUCoiLwMJvA6+wpivZkuiIvWY7bVGYsEYrrW+bCNN9wCGYTZEyX++os9 -v/38wdOqGUT00ewXkjIUFCWbrnxxSr98kF3w3wPf9K4Y40MNxeR90nyX4zjXGF1/ -91msUh+iivsz9mcN9DK83fgTyOsoVLX5cm/L2UBwMacsfjBbN4djOc5IuYMar/VN -GQIDAQABo1MwUTAdBgNVHQ4EFgQUtkf+yAvqgZC8f22iJny9hFEDolMwHwYDVR0j -BBgwFoAUtkf+yAvqgZC8f22iJny9hFEDolMwDwYDVR0TAQH/BAUwAwEB/zANBgkq -hkiG9w0BAQsFAAOCAgEAE2QasBVru618rxupyJgEHw6r4iv7sz1Afz3Q5qJ4oSA9 -xVsrVCjr3iHRFSw8Rf670E8Ffk/JjzS65mHw6zeZj/ANBKQWLjRlqzYXeetq5HzG -SIgaG7p1RFvvzz3+leFGzjinZ6sKbfB4OB72o2YN+fO8DsDxgGKll0W4KAazizSe -HY9Pgu437tWnwF16rFO3IL47n5HzYlRoGIPOpzFoNX5+fyn9GlnKEtONF2QBKTjY -rdjvqFRByDiC74d8z/Yx8IiDRn1mTcG90JLR9+c6M7fruha9Y/rJfw+4AhVh5ZDz -Bl9rGPjwEs5zwutYvVAJzs7AVcighYP1lHKoJ7DxBDQeyBsYlUNk2l6bmZgLgGUZ -+2OyWlqc/jD2GdDsIaZ4i7QqhTI/6aYZIf5zUkblKV1aMSaDulKxRv//OwW28Jax -9EEoV7VaFb3sOkB/tZGhusXeQVtdrhahT3KkZLNwmNXoXWKJ5LjeUlFWJyV6JbDe -y/PIWWCwWqyuFCSZS+Cg3RDgAzfSxkI8uVZ+IKKJS3UluDX45lxXtbRrvTQ+oDrA -6ga5c1Vz9C4kn1K5yW4d7QIvg6vPiy7gvl+//sz9oxUM3yswInDBY0HKLgT0Uq9b -YzLDh2RSaHsgHMPy2BKqR+q2N+lpg7inAWuJM1Huq6eHFqhiyQkzsfscBd1Dpm8= ------END CERTIFICATE----- diff --git a/_examples/file-server/http2push/mykey.key b/_examples/file-server/http2push/mykey.key deleted file mode 100644 index 39f7b3af50..0000000000 --- a/_examples/file-server/http2push/mykey.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDlVGyGAQ9uyfNb -wZyrtYOSjLpxf5NpNToh2OzU7gy2OexBji5lmWBQ3oYDG+FjAkbHORPzOMNpeMwj -e+IjGZBw8x6E+8WoGdSzbrEZ6pUVwKJGKEuDlx6g6HEmtv3ZwgGe20gvPjjW+oCO -888dwK/mbIHrHTq4nO3o0gAdAJwuamn9BlHU5O4RW7BQ4tLF+j/fBCACWRG1NHXA -0AT8eg544GyCdyteAH11oCDsHS8/DAPsM6t+tZrMCIt9+9dzPdVoOmQNaMMrcz8e -JohddRTK6zHe9ixZTt/soayOF7OSQQeekbr3HPYhD450zRVplLMHx7wnph/+O+Po -6bqDnUzdnkqAAwwymQapHMuHXZKNrhdfKau3rVo1GeXLIRgeWLUoxFSm4TYshrgt -+0AidLRH+dCY7MS9Ngga/sAK3vIDgSF75mFgOhY+q7nvY9Ecao6TnoNNRY29hUat -4y0VwSyysUy887vHr6lMK5CrAT/lCh8fuu20HUCoiLwMJvA6+wpivZkuiIvWY7bV -GYsEYrrW+bCNN9wCGYTZEyX++os9v/38wdOqGUT00ewXkjIUFCWbrnxxSr98kF3w -3wPf9K4Y40MNxeR90nyX4zjXGF1/91msUh+iivsz9mcN9DK83fgTyOsoVLX5cm/L -2UBwMacsfjBbN4djOc5IuYMar/VNGQIDAQABAoICAQCtWx1SSxjkcerxsLEDKApW -zOTfiUXgoOjZz0ZwS6b2VWDfyWAPU1r4ps39KaU+F+lzDhWjpYQqhbMjG7G9QMTs -bQvkEQLAaQ5duU5NPgQG1oCUsj8rMSBpGGz4jBnm834QHMk7VTjYYbKu3WTyo8cU -U2/+UDEkfxRlC+IkCmMFv1FxgMZ5PbktC/eDnYMhP2Pq7Q5ZWAVHymk9IMK0LHwm -Kdg842K4A3zTXwGkGwetDCMm+YQpG5TxqX/w82BRcCuTR5h8fnYSsWLEIvKwWyIl -ppcjaUnrFPG2yhxLqWUIKPpehuEjjhQMt9rDNoh6MHsJZZY5Dp5eq91EIvLoLQ99 -hXBmD4P8LDop4r0jniPZJi/ACsaD0jBooA4525+Kouq7RP28Jp/pek7lVOOcBgRv -D3zyESbKfqoaOfyfQ2ff4sILnTAr4V2nq3ekphGEYJrWN0ZoADcLdnr1cZ8L+VBI -o/4mi5/3HID/UEDliHSa97hxxGBEqTto0ZuXuNwfwx5ho33uVT6zNwRgiJ62Bgu3 -Fhk/wVGuZxWvb1KHUNInG9cvsslhO4Vu9wJvYj91BnRq36rsyKKid5DrU+PNgmog -lw3IXQpTojyRCYPuG9TKqEZ6b+so7GTKhBOjiwaupMOletVRGSAdbE81VN6HtxNW -aj39+FnxzMAlsieib+PBAQKCAQEA+t1fOYSaZBo7pZUmo2S0zulUEJjrYRGKJlWJ -4psWSwFu/7/3UL4q0RBQaSRew9u/YSpaNlBYfcpnFVOjiLwHq5Hx46Eq0BuKsNlJ -1/qxw9qjHqcrOre6K4/7NaWLPuM9fEmV+3MhFVXgv+WC5BHOowRTlOG30vIcC1J2 -L5xsBUsxDDY13cD1bLKRmFcyMFM8y7wMZmo7H/WfVmyoPKQaC43pTcmIXH0Jr2Ws -Wsfh18mhjtamaOPEFx5K0x4d0PI8tW5ouiUUkVIDaue27XfS969qEChv768/44eX -WeqcekaG9jv2noMClt79rYd3Lne9HkgY6IT9FT+JqXfu+KYwuQKCAQEA6gYzUsGB -9GQO8DE8AYn7JwNOtg1X4zKakXiGxH+nuZb7wJjAeGdYqTHySxPBXg0A2nDwoyz5 -4sAdLAr3FZoIvTzo7M5KIKFDzfyDmQDavhroH1mBAEiqKGNniP+RND3nWBBqDK1R -qcqbhI3Kj5Ycany6a4nP+hZRBIyT9sfJ0S0YruSY8IGXgDwhlJrZ7bsWMZylrgD/ -1qnPL0KqVBY8YR8msRj88h72IlD5o0kwvisOIvyhA0YgwGBb6lg7A+DifiF03ZlS -2yELbIkKDVr+p3jC7MBh4B+OJY68AMl6wVjAaDM1AZnpjKE5YmZg5+Ks5823zILo -PrSB9hn0+DIPYQKCAQEAh9x+JuNmzhHa/dkiHNl8hpadHYQD7gUWwZ4P1/bQAv0a -xU2MvmDPRXxFYDv/SqlnI1NRmhq3YiDM5SLv7SyQJt4al4IAcsaHvTFgqaSuw3hU -YVR9uAYqwE7w6OPn3r4o3Xfoz05Ru4FP//1nfucZ9vVv4rC/4nGWuJcHRM+9PLy1 -KnztfVR0VlL7QPrwRnW99kS4nnqn3K4khiTAlF73cAyCLsuXmydoqGIzDtMzv68G -XRpo82NvHmoccevcj/2w3T2XYECWvAEjsrEdQ8xiKBwLIAcWYEOUIUCcumiyKBKs -IwzkioI/U8AeuO0lobfdZ1n6i2sCuZA4mNxIQseWmQKCAQEA5YkfXdQeuq5JWJ1x -1bCYfjNoSHfd9CH2KSimRqVOxWGpm8Y3QeFbvNgYZjsCNlVauOZ9oA7FKfp0onY+ -0xk56SKM83eCjW6fKrK6AKAt7LhHZDhNpxGek+6r5luE+FCfUGkJG1YD+x2WW/UW -8K6zQF8GGeQZ8Zlh7axUlIBxGpG43BGrUHpLNqPD7BXWGq6dnhufBYRFay8y34/r -sH3+yuPa92ki7/geQppZwCZRgLSKMRbIdoWaKhZZEQlpGOzCOiRmk9OGyRcoNVRU -X7UYgPqZdc1cMo/AxGWzULJNjMaYMZvIKcHkqOKZfkIcWlSictn7pMPhN1+k+NWM -yMORAQKCAQAyXl02h/c2ihx6cjKlnNeDr2ZfzkoiAvFuKaoAR+KVvb9F9X7ZgKSi -wudZyelTglIVCYXeRmG09uX3rNGCzFrweRwgn6x/8DnN5pMRJVZOXFdgR+V9uKep -K6F7DYbPyggvLOAsezB+09i9lwxM+XdA2whVpL5NFR1rGfFglnE1EQHcEvNONkcv -0h8x9cNSptJyRDLiTIKI9EhonuzwzkGpvjULQE8MLbT8PbjoLFINcE9ZWhwtyw0V -XO32KE8iLKt3KzHz9CfTRCI3M7DwD752AC6zRr8ZS/HXzs+5WTkdVVEtRC7Abd3y -W2TzuSMYNDu876twbTVQJED3mwOAQ3J7 ------END PRIVATE KEY----- diff --git a/_examples/file-server/send-files/files/first.zip b/_examples/file-server/send-files/files/first.zip deleted file mode 100644 index c98f5ac8f5..0000000000 Binary files a/_examples/file-server/send-files/files/first.zip and /dev/null differ diff --git a/_examples/file-server/send-files/main.go b/_examples/file-server/send-files/main.go deleted file mode 100644 index 3e5c46a174..0000000000 --- a/_examples/file-server/send-files/main.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - app.Logger().SetLevel("debug") - - app.Get("/", download) - app.Get("/download", downloadWithRateLimit) - - app.Listen(":8080") -} - -func download(ctx iris.Context) { - src := "./files/first.zip" - ctx.SendFile(src, "client.zip") -} - -func downloadWithRateLimit(ctx iris.Context) { - // REPLACE THAT WITH A BIG LOCAL FILE OF YOUR OWN. - src := "./files/first.zip" - dest := "" /* optionally, keep it empty to resolve the filename based on the "src" */ - - // Limit download speed to ~50Kb/s with a burst of 100KB. - limit := 50.0 * iris.KB - burst := 100 * iris.KB - ctx.SendFileWithRate(src, dest, limit, burst) -} diff --git a/_examples/file-server/single-page-application/basic/main.go b/_examples/file-server/single-page-application/basic/main.go deleted file mode 100644 index 39315e16ae..0000000000 --- a/_examples/file-server/single-page-application/basic/main.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -// same as embedded-single-page-application but without go-bindata, the files are "physical" stored in the -// current system directory. - -var page = struct { - Title string -}{"Welcome"} - -func newApp() *iris.Application { - app := iris.New() - app.RegisterView(iris.HTML("./public", ".html")) - - app.Get("/", func(ctx iris.Context) { - ctx.ViewData("Page", page) - ctx.View("index.html") - }) - - app.HandleDir("/", iris.Dir("./public")) - - return app -} - -func main() { - app := newApp() - - // http://localhost:8080 - // http://localhost:8080/index.html - // http://localhost:8080/app.js - // http://localhost:8080/css/main.css - app.Listen(":8080") -} diff --git a/_examples/file-server/single-page-application/basic/main_test.go b/_examples/file-server/single-page-application/basic/main_test.go deleted file mode 100644 index 0ca13efa9d..0000000000 --- a/_examples/file-server/single-page-application/basic/main_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package main - -import ( - "io/ioutil" - "path/filepath" - "strings" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -type resource string - -func (r resource) String() string { - return string(r) -} - -func (r resource) strip(strip string) string { - s := r.String() - return strings.TrimPrefix(s, strip) -} - -func (r resource) loadFromBase(dir string) string { - filename := r.String() - - if filename == "/" { - filename = "/index.html" - } - - fullpath := filepath.Join(dir, filename) - - b, err := ioutil.ReadFile(fullpath) - if err != nil { - panic(fullpath + " failed with error: " + err.Error()) - } - - result := string(b) - - return result -} - -var urls = []resource{ - "/", - "/index.html", - "/app.js", - "/css/main.css", -} - -func TestSPA(t *testing.T) { - app := newApp() - e := httptest.New(t, app, httptest.Debug(false)) - - for _, u := range urls { - url := u.String() - contents := u.loadFromBase("./public") - contents = strings.Replace(contents, "{{ .Page.Title }}", page.Title, 1) - - e.GET(url).Expect(). - Status(httptest.StatusOK). - Body().Equal(contents) - } -} diff --git a/_examples/file-server/single-page-application/basic/public/app.js b/_examples/file-server/single-page-application/basic/public/app.js deleted file mode 100644 index c47a1fd53e..0000000000 --- a/_examples/file-server/single-page-application/basic/public/app.js +++ /dev/null @@ -1 +0,0 @@ -window.alert("app.js loaded from \"/"); \ No newline at end of file diff --git a/_examples/file-server/single-page-application/basic/public/css/main.css b/_examples/file-server/single-page-application/basic/public/css/main.css deleted file mode 100644 index fb72e54a97..0000000000 --- a/_examples/file-server/single-page-application/basic/public/css/main.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: black; -} diff --git a/_examples/file-server/single-page-application/basic/public/index.html b/_examples/file-server/single-page-application/basic/public/index.html deleted file mode 100644 index c52c161302..0000000000 --- a/_examples/file-server/single-page-application/basic/public/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - {{ .Page.Title }} - - - -

Hello from index.html

- - - - - - \ No newline at end of file diff --git a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/bindata.go b/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/bindata.go deleted file mode 100644 index 0fc5312b4d..0000000000 --- a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/bindata.go +++ /dev/null @@ -1,380 +0,0 @@ -// Code generated by go-bindata. (@generated) DO NOT EDIT. - -//Package main generated by go-bindata.// sources: -// public/app.js -// public/css/main.css -// public/index.html -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data []byte, name string) ([]byte, error) { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -// Name return file name -func (fi bindataFileInfo) Name() string { - return fi.name -} - -// Size return file size -func (fi bindataFileInfo) Size() int64 { - return fi.size -} - -// Mode return file mode -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} - -// ModTime return file modify time -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} - -// IsDir return file whether a directory -func (fi bindataFileInfo) IsDir() bool { - return fi.mode&os.ModeDir != 0 -} - -// Sys return file is sys mode -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -type assetFile struct { - *bytes.Reader - name string - childInfos []os.FileInfo - childInfoOffset int -} - -type assetOperator struct{} - -// Open implement http.FileSystem interface -func (f *assetOperator) Open(name string) (http.File, error) { - var err error - if len(name) > 0 && name[0] == '/' { - name = name[1:] - } - content, err := Asset(name) - if err == nil { - return &assetFile{name: name, Reader: bytes.NewReader(content)}, nil - } - children, err := AssetDir(name) - if err == nil { - childInfos := make([]os.FileInfo, 0, len(children)) - for _, child := range children { - childPath := filepath.Join(name, child) - info, errInfo := AssetInfo(filepath.Join(name, child)) - if errInfo == nil { - childInfos = append(childInfos, info) - } else { - childInfos = append(childInfos, newDirFileInfo(childPath)) - } - } - return &assetFile{name: name, childInfos: childInfos}, nil - } else { - // If the error is not found, return an error that will - // result in a 404 error. Otherwise the server returns - // a 500 error for files not found. - if strings.Contains(err.Error(), "not found") { - return nil, os.ErrNotExist - } - return nil, err - } -} - -// Close no need do anything -func (f *assetFile) Close() error { - return nil -} - -// Readdir read dir's children file info -func (f *assetFile) Readdir(count int) ([]os.FileInfo, error) { - if len(f.childInfos) == 0 { - return nil, os.ErrNotExist - } - if count <= 0 { - return f.childInfos, nil - } - if f.childInfoOffset+count > len(f.childInfos) { - count = len(f.childInfos) - f.childInfoOffset - } - offset := f.childInfoOffset - f.childInfoOffset += count - return f.childInfos[offset : offset+count], nil -} - -// Stat read file info from asset item -func (f *assetFile) Stat() (os.FileInfo, error) { - if len(f.childInfos) != 0 { - return newDirFileInfo(f.name), nil - } - return AssetInfo(f.name) -} - -// newDirFileInfo return default dir file info -func newDirFileInfo(name string) os.FileInfo { - return &bindataFileInfo{ - name: name, - size: 0, - mode: os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir - modTime: time.Time{}} -} - -// AssetFile return a http.FileSystem instance that data backend by asset -func AssetFile() http.FileSystem { - return &assetOperator{} -} - -var _appJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2a\xcf\xcc\x4b\xc9\x2f\xd7\x4b\xcc\x49\x2d\x2a\xd1\x50\x4a\x2c\x28\xd0\xcb\x2a\x56\xc8\xc9\x4f\x4c\x49\x4d\x51\x48\x2b\xca\xcf\x55\x88\x51\xd2\x57\xd2\xb4\x06\x04\x00\x00\xff\xff\xa9\x06\xf7\xa3\x27\x00\x00\x00") - -func appJsBytes() ([]byte, error) { - return bindataRead( - _appJs, - "app.js", - ) -} - -func appJs() (*asset, error) { - bytes, err := appJsBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "app.js", size: 39, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _cssMainCss = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xca\x4f\xa9\x54\xa8\xe6\xe5\x52\x50\x50\x50\x48\x4a\x4c\xce\x4e\x2f\xca\x2f\xcd\x4b\xd1\x4d\xce\xcf\xc9\x2f\xb2\x52\x48\xca\x49\x4c\xce\xb6\xe6\xe5\xaa\xe5\xe5\x02\x04\x00\x00\xff\xff\x03\x25\x9c\x89\x29\x00\x00\x00") - -func cssMainCssBytes() ([]byte, error) { - return bindataRead( - _cssMainCss, - "css/main.css", - ) -} - -func cssMainCss() (*asset, error) { - bytes, err := cssMainCssBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "css/main.css", size: 41, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _indexHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4c\x8e\x41\x0e\xc2\x20\x10\x45\xf7\x24\xdc\xe1\xa7\x07\x28\xe9\x7e\x64\xed\x35\x10\x46\xc1\x50\x21\x30\x0b\xbd\xbd\x29\xc5\xc4\xf5\x7f\x6f\xde\x50\x94\x3d\x5b\xad\xb4\xa2\xc8\x2e\x58\xad\x00\x80\x24\x49\x66\x7b\xe5\x9c\x0b\xee\xad\xec\xe8\xe2\x24\x79\x54\xf7\x60\x32\xe7\xaa\x15\x99\xe9\x68\x45\xb7\x12\x3e\x3f\x3b\x6e\x16\x7f\x6e\x7a\x05\x7e\xaf\x47\x08\x64\xe2\x36\xf8\x49\x76\xdf\x52\x15\xf4\xe6\x2f\x8b\x71\xb5\xae\xcf\xbe\x58\x80\xcc\x39\x8c\xc6\xbc\x3c\x72\xc7\xb3\xdf\x00\x00\x00\xff\xff\x7e\xad\xd1\x97\xb3\x00\x00\x00") - -func indexHtmlBytes() ([]byte, error) { - return bindataRead( - _indexHtml, - "index.html", - ) -} - -func indexHtml() (*asset, error) { - bytes, err := indexHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "index.html", size: 179, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "app.js": appJs, - "css/main.css": cssMainCss, - "index.html": indexHtml, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"} -// AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("notexist") would return an error -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "app.js": {appJs, map[string]*bintree{}}, - "css": {nil, map[string]*bintree{ - "main.css": {cssMainCss, map[string]*bintree{}}, - }}, - "index.html": {indexHtml, map[string]*bintree{}}, -}} - -// RestoreAsset restores an asset under the given directory -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil -} - -// RestoreAssets restores an asset under the given directory recursively -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) -} diff --git a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go b/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go deleted file mode 100644 index 5e716f020c..0000000000 --- a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/main.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata -// $ go-bindata -fs -prefix "public" ./public/... -// $ go run . - -func newApp() *iris.Application { - app := iris.New() - app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) { - ctx.Writef("404 not found here") - }) - - app.HandleDir("/", AssetFile()) - - // Note: - // if you want a dynamic index page then see the file-server/embedded-single-page-application - // which is registering a view engine based on bindata as well and a root route. - - app.Get("/ping", func(ctx iris.Context) { - ctx.WriteString("pong") - }) - app.Get("/.well-known", func(ctx iris.Context) { - ctx.WriteString("well-known") - }) - app.Get(".well-known/ready", func(ctx iris.Context) { - ctx.WriteString("ready") - }) - app.Get(".well-known/live", func(ctx iris.Context) { - ctx.WriteString("live") - }) - app.Get(".well-known/metrics", func(ctx iris.Context) { - ctx.Writef("metrics") - }) - return app -} - -func main() { - app := newApp() - - // http://localhost:8080/index.html - // http://localhost:8080/app.js - // http://localhost:8080/css/main.css - // - // http://localhost:8080/ping - // http://localhost:8080/.well-known - // http://localhost:8080/.well-known/ready - // http://localhost:8080/.well-known/live - // http://localhost:8080/.well-known/metrics - // - // Remember: we could use the root wildcard `app.Get("/{param:path}")` and serve the files manually as well. - app.Listen(":8080") -} diff --git a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/public/app.js b/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/public/app.js deleted file mode 100644 index c009a1b94c..0000000000 --- a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/public/app.js +++ /dev/null @@ -1 +0,0 @@ -window.alert("app.js loaded from static page of \"/"); \ No newline at end of file diff --git a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/public/css/main.css b/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/public/css/main.css deleted file mode 100644 index 7db3df1d78..0000000000 --- a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/public/css/main.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: black; -} diff --git a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/public/index.html b/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/public/index.html deleted file mode 100644 index e36f1bfefb..0000000000 --- a/_examples/file-server/single-page-application/embedded-single-page-application-with-other-routes/public/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - Hello from static page - - - -

Hello from index.html

- - - - - - \ No newline at end of file diff --git a/_examples/file-server/single-page-application/embedded-single-page-application/bindata.go b/_examples/file-server/single-page-application/embedded-single-page-application/bindata.go deleted file mode 100644 index c9cf99a6f2..0000000000 --- a/_examples/file-server/single-page-application/embedded-single-page-application/bindata.go +++ /dev/null @@ -1,407 +0,0 @@ -// Code generated by go-bindata. (@generated) DO NOT EDIT. - -//Package main generated by go-bindata.// sources: -// public/app.js -// public/app2/index.html -// public/css/main.css -// public/index.html -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data, name string) ([]byte, error) { - gz, err := gzip.NewReader(strings.NewReader(data)) - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -// Name return file name -func (fi bindataFileInfo) Name() string { - return fi.name -} - -// Size return file size -func (fi bindataFileInfo) Size() int64 { - return fi.size -} - -// Mode return file mode -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} - -// ModTime return file modify time -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} - -// IsDir return file whether a directory -func (fi bindataFileInfo) IsDir() bool { - return fi.mode&os.ModeDir != 0 -} - -// Sys return file is sys mode -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -type assetFile struct { - *bytes.Reader - name string - childInfos []os.FileInfo - childInfoOffset int -} - -type assetOperator struct{} - -// Open implement http.FileSystem interface -func (f *assetOperator) Open(name string) (http.File, error) { - var err error - if len(name) > 0 && name[0] == '/' { - name = name[1:] - } - content, err := Asset(name) - if err == nil { - return &assetFile{name: name, Reader: bytes.NewReader(content)}, nil - } - children, err := AssetDir(name) - if err == nil { - childInfos := make([]os.FileInfo, 0, len(children)) - for _, child := range children { - childPath := filepath.Join(name, child) - info, errInfo := AssetInfo(filepath.Join(name, child)) - if errInfo == nil { - childInfos = append(childInfos, info) - } else { - childInfos = append(childInfos, newDirFileInfo(childPath)) - } - } - return &assetFile{name: name, childInfos: childInfos}, nil - } else { - // If the error is not found, return an error that will - // result in a 404 error. Otherwise the server returns - // a 500 error for files not found. - if strings.Contains(err.Error(), "not found") { - return nil, os.ErrNotExist - } - return nil, err - } -} - -// Close no need do anything -func (f *assetFile) Close() error { - return nil -} - -// Readdir read dir's children file info -func (f *assetFile) Readdir(count int) ([]os.FileInfo, error) { - if len(f.childInfos) == 0 { - return nil, os.ErrNotExist - } - if count <= 0 { - return f.childInfos, nil - } - if f.childInfoOffset+count > len(f.childInfos) { - count = len(f.childInfos) - f.childInfoOffset - } - offset := f.childInfoOffset - f.childInfoOffset += count - return f.childInfos[offset : offset+count], nil -} - -// Stat read file info from asset item -func (f *assetFile) Stat() (os.FileInfo, error) { - if len(f.childInfos) != 0 { - return newDirFileInfo(f.name), nil - } - return AssetInfo(f.name) -} - -// newDirFileInfo return default dir file info -func newDirFileInfo(name string) os.FileInfo { - return &bindataFileInfo{ - name: name, - size: 0, - mode: os.FileMode(2147484068), // equal os.FileMode(0644)|os.ModeDir - modTime: time.Time{}} -} - -// AssetFile return a http.FileSystem instance that data backend by asset -func AssetFile() http.FileSystem { - return &assetOperator{} -} - -var _publicAppJs = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2a\xcf\xcc\x4b\xc9\x2f\xd7\x4b\xcc\x49\x2d\x2a\xd1\x50\x4a\x2c\x28\xd0\xcb\x2a\x56\xc8\xc9\x4f\x4c\x49\x4d\x51\x48\x2b\xca\xcf\x55\x88\x51\xd2\x57\xd2\xb4\x06\x04\x00\x00\xff\xff\xa9\x06\xf7\xa3\x27\x00\x00\x00" - -func publicAppJsBytes() ([]byte, error) { - return bindataRead( - _publicAppJs, - "public/app.js", - ) -} - -func publicAppJs() (*asset, error) { - bytes, err := publicAppJsBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "public/app.js", size: 39, mode: os.FileMode(438), modTime: time.Unix(1595516291, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _publicApp2IndexHtml = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\x8d\x41\x0a\xc2\x40\x0c\x45\xf7\x81\xdc\xe1\x9f\xc0\xd0\xae\x43\xc0\x9d\xd7\xa8\x4c\x24\x85\xd4\x09\x32\x0b\xbd\xbd\xb4\xd6\xe5\x87\xf7\xde\xd7\x18\x5b\x1a\x13\x93\x86\x2f\xcd\x98\x00\x40\xc7\x3a\xd2\xed\x5a\x85\x59\xe5\x37\x98\x54\x4e\x84\x49\xef\xbd\x7d\xfe\x70\x4c\x86\x9b\x67\x76\x3c\x5e\x7d\xc3\x52\x35\xcb\xfa\x6c\xfe\xbe\xec\x71\xa8\xc4\x74\xd8\xa7\x73\x84\xf6\xd7\x6f\x00\x00\x00\xff\xff\xfd\x28\x92\x95\x7c\x00\x00\x00" - -func publicApp2IndexHtmlBytes() ([]byte, error) { - return bindataRead( - _publicApp2IndexHtml, - "public/app2/index.html", - ) -} - -func publicApp2IndexHtml() (*asset, error) { - bytes, err := publicApp2IndexHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "public/app2/index.html", size: 124, mode: os.FileMode(438), modTime: time.Unix(1572105320, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _publicCssMainCss = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xca\x4f\xa9\x54\xa8\xe6\xe5\x52\x50\x50\x50\x48\x4a\x4c\xce\x4e\x2f\xca\x2f\xcd\x4b\xd1\x4d\xce\xcf\xc9\x2f\xb2\x52\x48\xca\x49\x4c\xce\xb6\xe6\xe5\xaa\xe5\xe5\x02\x04\x00\x00\xff\xff\x03\x25\x9c\x89\x29\x00\x00\x00" - -func publicCssMainCssBytes() ([]byte, error) { - return bindataRead( - _publicCssMainCss, - "public/css/main.css", - ) -} - -func publicCssMainCss() (*asset, error) { - bytes, err := publicCssMainCssBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "public/css/main.css", size: 41, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _publicIndexHtml = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\x8e\x41\x0e\xc2\x20\x10\x45\xf7\x24\xdc\xe1\xa7\x07\x80\x74\x3f\xb2\x76\xe9\xc2\x0b\x60\x41\xc1\x50\x21\xc0\x42\xd3\xf4\xee\x06\x4a\x97\x93\xf7\x66\xde\x90\xab\x6b\x50\x9c\x71\x46\xce\x6a\xa3\x38\x03\x00\xaa\xbe\x06\xab\xb6\x0d\xe2\xa6\x5f\x56\xdc\xdb\x88\x7d\x27\x79\x00\xce\x48\x0e\x9d\x33\x7a\x44\xf3\x3b\x17\xdd\xac\x70\xb5\x21\x44\x3c\x73\x5c\xe1\x3f\xc6\x7e\x45\x6b\x80\xa4\x9b\xbb\x3f\xcc\xb2\x64\x9f\x2a\x4a\x5e\x2e\x93\xd4\x29\x89\x77\x99\x14\x40\xf2\x00\xbd\x31\x2e\xf7\x5c\xfb\xf3\x1f\x00\x00\xff\xff\x25\xe9\x37\x57\xae\x00\x00\x00" - -func publicIndexHtmlBytes() ([]byte, error) { - return bindataRead( - _publicIndexHtml, - "public/index.html", - ) -} - -func publicIndexHtml() (*asset, error) { - bytes, err := publicIndexHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "public/index.html", size: 174, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "public/app.js": publicAppJs, - "public/app2/index.html": publicApp2IndexHtml, - "public/css/main.css": publicCssMainCss, - "public/index.html": publicIndexHtml, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"} -// AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("notexist") would return an error -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "public": {nil, map[string]*bintree{ - "app.js": {publicAppJs, map[string]*bintree{}}, - "app2": {nil, map[string]*bintree{ - "index.html": {publicApp2IndexHtml, map[string]*bintree{}}, - }}, - "css": {nil, map[string]*bintree{ - "main.css": {publicCssMainCss, map[string]*bintree{}}, - }}, - "index.html": {publicIndexHtml, map[string]*bintree{}}, - }}, -}} - -// RestoreAsset restores an asset under the given directory -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil -} - -// RestoreAssets restores an asset under the given directory recursively -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) -} diff --git a/_examples/file-server/single-page-application/embedded-single-page-application/main.go b/_examples/file-server/single-page-application/embedded-single-page-application/main.go deleted file mode 100644 index f6ff005561..0000000000 --- a/_examples/file-server/single-page-application/embedded-single-page-application/main.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata -// $ go-bindata -nomemcopy -fs ./public/... -// $ go run . - -var page = struct { - Title string -}{"Welcome"} - -func newApp() *iris.Application { - app := iris.New() - app.RegisterView(iris.HTML("./public", ".html").Binary(Asset, AssetNames)) - - app.Get("/", func(ctx iris.Context) { - ctx.ViewData("Page", page) - ctx.View("index.html") - }) - - // We didn't add a `-prefix "public"` argument on go-bindata command - // because the view's `Assset` and `AssetNames` require fullpath. - // Make use of the `PrefixDir` to serve assets on cases like that; - // when bindata.go file contains files that are - // not necessary public assets to be served. - app.HandleDir("/", iris.PrefixDir("public", AssetFile())) - - return app -} - -func main() { - app := newApp() - - // http://localhost:8080 - // http://localhost:8080/app.js - // http://localhost:8080/css/main.css - // http://localhost:8080/app2 - app.Listen(":8080") -} diff --git a/_examples/file-server/single-page-application/embedded-single-page-application/main_test.go b/_examples/file-server/single-page-application/embedded-single-page-application/main_test.go deleted file mode 100644 index 89270787c2..0000000000 --- a/_examples/file-server/single-page-application/embedded-single-page-application/main_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package main - -import ( - "io/ioutil" - "path/filepath" - "runtime" - "strings" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -type resource string - -func (r resource) contentType() string { - switch filepath.Ext(r.String()) { - case ".js": - return "text/javascript" - case ".css": - return "text/css" - default: - return "text/html" - } -} - -func (r resource) String() string { - return string(r) -} - -func (r resource) strip(strip string) string { - s := r.String() - return strings.TrimPrefix(s, strip) -} - -func (r resource) loadFromBase(dir string) string { - filename := r.String() - - if strings.HasSuffix(filename, "/") { - filename = filename + "index.html" - } - - fullpath := filepath.Join(dir, filename) - - b, err := ioutil.ReadFile(fullpath) - if err != nil { - panic(fullpath + " failed with error: " + err.Error()) - } - result := string(b) - if runtime.GOOS != "windows" { - result = strings.Replace(result, "\n", "\r\n", -1) - result = strings.Replace(result, "\r\r", "", -1) - } - return result -} - -var urls = []resource{ - "/", - "/index.html", - "/app.js", - "/css/main.css", - "/app2/", - "/app2/index.html", -} - -func TestSPAEmbedded(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - for _, u := range urls { - url := u.String() - contents := u.loadFromBase("./public") - contents = strings.Replace(contents, "{{ .Page.Title }}", page.Title, 1) - - e.GET(url).Expect(). - Status(httptest.StatusOK). - ContentType(u.contentType(), app.ConfigurationReadOnly().GetCharset()). - Body().Equal(contents) - } -} diff --git a/_examples/file-server/single-page-application/embedded-single-page-application/public/app.js b/_examples/file-server/single-page-application/embedded-single-page-application/public/app.js deleted file mode 100644 index c47a1fd53e..0000000000 --- a/_examples/file-server/single-page-application/embedded-single-page-application/public/app.js +++ /dev/null @@ -1 +0,0 @@ -window.alert("app.js loaded from \"/"); \ No newline at end of file diff --git a/_examples/file-server/single-page-application/embedded-single-page-application/public/app2/index.html b/_examples/file-server/single-page-application/embedded-single-page-application/public/app2/index.html deleted file mode 100644 index 680dd24274..0000000000 --- a/_examples/file-server/single-page-application/embedded-single-page-application/public/app2/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - App 2 - - - -

Hello from app2/index.html

- - - \ No newline at end of file diff --git a/_examples/file-server/single-page-application/embedded-single-page-application/public/css/main.css b/_examples/file-server/single-page-application/embedded-single-page-application/public/css/main.css deleted file mode 100644 index fb72e54a97..0000000000 --- a/_examples/file-server/single-page-application/embedded-single-page-application/public/css/main.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: black; -} diff --git a/_examples/file-server/single-page-application/embedded-single-page-application/public/index.html b/_examples/file-server/single-page-application/embedded-single-page-application/public/index.html deleted file mode 100644 index c52c161302..0000000000 --- a/_examples/file-server/single-page-application/embedded-single-page-application/public/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - {{ .Page.Title }} - - - -

Hello from index.html

- - - - - - \ No newline at end of file diff --git a/_examples/file-server/subdomain/assets/app2/app22/just_a_text_no_index.txt b/_examples/file-server/subdomain/assets/app2/app22/just_a_text_no_index.txt deleted file mode 100644 index f1cc1278cf..0000000000 --- a/_examples/file-server/subdomain/assets/app2/app22/just_a_text_no_index.txt +++ /dev/null @@ -1 +0,0 @@ -just a text. \ No newline at end of file diff --git a/_examples/file-server/subdomain/assets/app2/app2app3/index.html b/_examples/file-server/subdomain/assets/app2/app2app3/index.html deleted file mode 100644 index 750f10bacb..0000000000 --- a/_examples/file-server/subdomain/assets/app2/app2app3/index.html +++ /dev/null @@ -1 +0,0 @@ -

Hello App2App3 index

\ No newline at end of file diff --git a/_examples/file-server/subdomain/assets/app2/index.html b/_examples/file-server/subdomain/assets/app2/index.html deleted file mode 100644 index 8193d82245..0000000000 --- a/_examples/file-server/subdomain/assets/app2/index.html +++ /dev/null @@ -1 +0,0 @@ -

Hello App2 index

\ No newline at end of file diff --git a/_examples/file-server/subdomain/assets/css/main.css b/_examples/file-server/subdomain/assets/css/main.css deleted file mode 100644 index 7db3df1d78..0000000000 --- a/_examples/file-server/subdomain/assets/css/main.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: black; -} diff --git a/_examples/file-server/subdomain/assets/favicon.ico b/_examples/file-server/subdomain/assets/favicon.ico deleted file mode 100644 index c370da518e..0000000000 Binary files a/_examples/file-server/subdomain/assets/favicon.ico and /dev/null differ diff --git a/_examples/file-server/subdomain/assets/index.html b/_examples/file-server/subdomain/assets/index.html deleted file mode 100644 index 06651a4581..0000000000 --- a/_examples/file-server/subdomain/assets/index.html +++ /dev/null @@ -1 +0,0 @@ -

Hello index

\ No newline at end of file diff --git a/_examples/file-server/subdomain/assets/js/jquery-2.1.1.js b/_examples/file-server/subdomain/assets/js/jquery-2.1.1.js deleted file mode 100644 index 07f9351406..0000000000 --- a/_examples/file-server/subdomain/assets/js/jquery-2.1.1.js +++ /dev/null @@ -1 +0,0 @@ -console.log("example"); \ No newline at end of file diff --git a/_examples/file-server/subdomain/hosts b/_examples/file-server/subdomain/hosts deleted file mode 100644 index 69c1a384a6..0000000000 --- a/_examples/file-server/subdomain/hosts +++ /dev/null @@ -1,2 +0,0 @@ -127.0.0.1 examle.com -127.0.0.1 v1.examle.com \ No newline at end of file diff --git a/_examples/file-server/subdomain/main.go b/_examples/file-server/subdomain/main.go deleted file mode 100644 index bae492f406..0000000000 --- a/_examples/file-server/subdomain/main.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -const ( - addr = "example.com:80" - subdomain = "v1" -) - -func newApp() *iris.Application { - app := iris.New() - app.Favicon("./assets/favicon.ico") - - v1 := app.Subdomain(subdomain) - v1.HandleDir("/", iris.Dir("./assets")) - - // http://v1.example.com - // http://v1.example.com/css/main.css - // http://v1.example.com/js/jquery-2.1.1.js - // http://v1.example.com/favicon.ico - return app -} - -func main() { - app := newApp() - app.Listen(addr) -} diff --git a/_examples/file-server/subdomain/main_test.go b/_examples/file-server/subdomain/main_test.go deleted file mode 100644 index 96eb882dc8..0000000000 --- a/_examples/file-server/subdomain/main_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package main - -import ( - "io/ioutil" - "net" - "path/filepath" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -type resource string - -func (r resource) contentType() string { - switch filepath.Ext(r.String()) { - case ".js": - return "text/javascript" - case ".css": - return "text/css" - case ".ico": - return "image/x-icon" - case ".html", "": - return "text/html" - default: - return "text/plain" - } -} - -func (r resource) String() string { - return string(r) -} - -func (r resource) loadFromBase(dir string) string { - filename := r.String() - - if filepath.Ext(filename) == "" { - // root /. - filename = filename + "/index.html" - } - - fullpath := filepath.Join(dir, filename) - - b, err := ioutil.ReadFile(fullpath) - if err != nil { - panic(fullpath + " failed with error: " + err.Error()) - } - - result := string(b) - - return result -} - -func TestFileServerSubdomainBasic(t *testing.T) { - urls := []resource{ - "/css/main.css", - "/js/jquery-2.1.1.js", - "/favicon.ico", - "/app2", - "/app2/app2app3", - "/", - } - - app := newApp() - e := httptest.New(t, app) - - host, _, err := net.SplitHostPort(addr) - if err != nil { - t.Fatal(err) - } - host = "http://" + subdomain + "." + host - - for _, u := range urls { - url := u.String() - contents := u.loadFromBase("./assets") - - e.GET(url).WithURL(host).Expect(). - Status(httptest.StatusOK). - ContentType(u.contentType(), app.ConfigurationReadOnly().GetCharset()). - Body().Equal(contents) - } -} diff --git a/_examples/file-server/upload-file/main.go b/_examples/file-server/upload-file/main.go deleted file mode 100644 index 0d34dc1670..0000000000 --- a/_examples/file-server/upload-file/main.go +++ /dev/null @@ -1,128 +0,0 @@ -package main - -import ( - "crypto/md5" - "fmt" - "io" - "os" - "strconv" - "time" - - "github.com/kataras/iris/v12" -) - -const maxSize = 5 << 20 // 5MB - -func main() { - app := iris.New() - - app.RegisterView(iris.HTML("./templates", ".html")) - - // Serve the upload_form.html to the client. - app.Get("/upload", func(ctx iris.Context) { - // create a token (optionally). - - now := time.Now().Unix() - h := md5.New() - io.WriteString(h, strconv.FormatInt(now, 10)) - token := fmt.Sprintf("%x", h.Sum(nil)) - - // render the form with the token for any use you'd like. - // ctx.ViewData("", token) - // or add second argument to the `View` method. - // Token will be passed as {{.}} in the template. - ctx.View("upload_form.html", token) - }) - - /* Read before continue. - - 0. The default post max size is 32MB, - you can extend it to read more data using the `iris.WithPostMaxMemory(maxSize)` configurator at `app.Run`, - note that this will not be enough for your needs, read below. - - 1. The faster way to check the size is using the `ctx.GetContentLength()` which returns the whole request's size - (plus a logical number like 2MB or even 10MB for the rest of the size like headers). You can create a - middleware to adapt this to any necessary handler. - - myLimiter := func(ctx iris.Context) { - if ctx.GetContentLength() > maxSize { // + 2 << 20 { - ctx.StatusCode(iris.StatusRequestEntityTooLarge) - return - } - ctx.Next() - } - - app.Post("/upload", myLimiter, myUploadHandler) - - Most clients will set the "Content-Length" header (like browsers) but it's always better to make sure that any client - can't send data that your server can't or doesn't want to handle. This can be happen using - the `app.Use(LimitRequestBodySize(maxSize))` (as app or route middleware) - or the `ctx.SetMaxRequestBodySize(maxSize)` to limit the request based on a customized logic inside a particular handler, they're the same, - read below. - - 2. You can force-limit the request body size inside a handler using the `ctx.SetMaxRequestBodySize(maxSize)`, - this will force the connection to close if the incoming data are larger (most clients will receive it as "connection reset"), - use that to make sure that the client will not send data that your server can't or doesn't want to accept, as a fallback. - - app.Post("/upload", iris.LimitRequestBodySize(maxSize), myUploadHandler) - - OR - - app.Post("/upload", func(ctx iris.Context){ - ctx.SetMaxRequestBodySize(maxSize) - - // [...] - }) - - 3. Another way is to receive the data and check the second return value's `Size` value of the `ctx.FormFile`, i.e `info.Size`, this will give you - the exact file size, not the whole incoming request data length. - - app.Post("/", func(ctx iris.Context){ - file, info, err := ctx.FormFile("uploadfile") - if err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.HTML("Error while uploading: " + err.Error() + "") - return - } - - defer file.Close() - - if info.Size > maxSize { - ctx.StatusCode(iris.StatusRequestEntityTooLarge) - return - } - - // [...] - }) - */ - - // Handle the post request from the upload_form.html to the server - app.Post("/upload", iris.LimitRequestBodySize(maxSize+1<<20), func(ctx iris.Context) { - // Get the file from the request. - file, info, err := ctx.FormFile("uploadfile") - if err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.HTML("Error while uploading: " + err.Error() + "") - return - } - - defer file.Close() - fname := info.Filename - - // Create a file with the same name - // assuming that you have a folder named 'uploads' - out, err := os.OpenFile("./uploads/"+fname, - os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.HTML("Error while uploading: " + err.Error() + "") - return - } - defer out.Close() - - io.Copy(out, file) - }) - - // start the server at http://localhost:8080 with post limit at 5 MB. - app.Listen(":8080" /* 0.*/, iris.WithPostMaxMemory(maxSize)) -} diff --git a/_examples/file-server/upload-file/templates/upload_form.html b/_examples/file-server/upload-file/templates/upload_form.html deleted file mode 100644 index aa60740fe3..0000000000 --- a/_examples/file-server/upload-file/templates/upload_form.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - Upload file - - - -
- - - -
- - - \ No newline at end of file diff --git a/_examples/file-server/upload-file/uploads/.gitkeep b/_examples/file-server/upload-file/uploads/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/_examples/file-server/upload-files/main.go b/_examples/file-server/upload-files/main.go deleted file mode 100644 index e1094daa87..0000000000 --- a/_examples/file-server/upload-files/main.go +++ /dev/null @@ -1,116 +0,0 @@ -package main - -import ( - "crypto/md5" - "fmt" - "io" - "mime/multipart" - "os" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/kataras/iris/v12" -) - -func main() { - app := newApp() - // start the server at http://localhost:8080 with post limit at 32 MB. - app.Listen(":8080", iris.WithPostMaxMemory(32<<20 /* same as 32 * iris.MB */)) -} - -func newApp() *iris.Application { - app := iris.New() - app.RegisterView(iris.HTML("./templates", ".html")) - - // Serve the upload_form.html to the client. - app.Get("/upload", func(ctx iris.Context) { - // create a token (optionally). - - now := time.Now().Unix() - h := md5.New() - io.WriteString(h, strconv.FormatInt(now, 10)) - token := fmt.Sprintf("%x", h.Sum(nil)) - - // render the form with the token for any use you'd like. - ctx.View("upload_form.html", token) - }) - - // Handle the post request from the upload_form.html to the server. - app.Post("/upload", func(ctx iris.Context) { - // - // UploadFormFiles - // uploads any number of incoming files ("multiple" property on the form input). - // - - // second argument is optional, - // it can be used to change a file's name based on the request, - // at this example we will showcase how to use it - // by prefixing the uploaded file with the current user's ip. - ctx.UploadFormFiles("./uploads", beforeSave) - }) - - app.Post("/upload_manual", func(ctx iris.Context) { - // Get the max post value size passed via iris.WithPostMaxMemory. - maxSize := ctx.Application().ConfigurationReadOnly().GetPostMaxMemory() - - err := ctx.Request().ParseMultipartForm(maxSize) - if err != nil { - ctx.StopWithError(iris.StatusInternalServerError, err) - return - } - - form := ctx.Request().MultipartForm - - files := form.File["files[]"] - failures := 0 - for _, file := range files { - _, err = saveUploadedFile(file, "./uploads") - if err != nil { - failures++ - ctx.Writef("failed to upload: %s\n", file.Filename) - } - } - ctx.Writef("%d files uploaded", len(files)-failures) - }) - - return app -} - -func saveUploadedFile(fh *multipart.FileHeader, destDirectory string) (int64, error) { - src, err := fh.Open() - if err != nil { - return 0, err - } - defer src.Close() - - out, err := os.OpenFile(filepath.Join(destDirectory, fh.Filename), - os.O_WRONLY|os.O_CREATE, os.FileMode(0666)) - if err != nil { - return 0, err - } - defer out.Close() - - return io.Copy(out, src) -} - -func beforeSave(ctx iris.Context, file *multipart.FileHeader) { - ip := ctx.RemoteAddr() - // make sure you format the ip in a way - // that can be used for a file name (simple case): - ip = strings.Replace(ip, ".", "_", -1) - ip = strings.Replace(ip, ":", "_", -1) - - // you can use the time.Now, to prefix or suffix the files - // based on the current time as well, as an exercise. - // i.e unixTime := time.Now().Unix() - // prefix the Filename with the $IP- - // no need for more actions, internal uploader will use this - // name to save the file into the "./uploads" folder. - if ip == "" { - return - } - - file.Filename = ip + "-" + file.Filename -} diff --git a/_examples/file-server/upload-files/main_test.go b/_examples/file-server/upload-files/main_test.go deleted file mode 100644 index 91eb874256..0000000000 --- a/_examples/file-server/upload-files/main_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "os" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestUploadFiles(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - // upload the file itself. - fh, err := os.Open("main.go") - if err != nil { - t.Fatal(err) - } - defer fh.Close() - - e.POST("/upload").WithMultipart().WithFile("files", "main.go", fh). - Expect().Status(httptest.StatusOK) - - f, err := os.Open("uploads/main.go") - if err != nil { - t.Fatalf("expected file to get actually uploaded on the system directory but: %v", err) - } - f.Close() - - os.Remove(f.Name()) -} diff --git a/_examples/file-server/upload-files/templates/upload_form.html b/_examples/file-server/upload-files/templates/upload_form.html deleted file mode 100644 index 0df624182a..0000000000 --- a/_examples/file-server/upload-files/templates/upload_form.html +++ /dev/null @@ -1,12 +0,0 @@ - - -Upload file - - -
- -
- - diff --git a/_examples/file-server/upload-files/uploads/.gitkeep b/_examples/file-server/upload-files/uploads/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/_examples/http-server/README.md b/_examples/http-server/README.md deleted file mode 100644 index d588a0c6c1..0000000000 --- a/_examples/http-server/README.md +++ /dev/null @@ -1,205 +0,0 @@ -# Hosts - -## Listen and Serve - -You can start the server(s) listening to any type of `net.Listener` or even `http.Server` instance. -The method for initialization of the server should be passed at the end, via `Run` function. - -The most common method that Go developers use to serve their servers are -by passing a network address with form of "hostname:ip". With Iris -we use the `iris.Addr` which is an `iris.Runner` type - -```go -// Listening on tcp with network address 0.0.0.0:8080 -// app.Listen(":8080") it's a shortcut of: -app.Run(iris.Addr(":8080")) -``` - -Sometimes you have created a standard net/http server somewhere else in your app and want to use that to serve the Iris web app - -```go -// Same as before but using a custom http.Server which may being used somewhere else too -app.Run(iris.Server(&http.Server{Addr:":8080"})) -``` - -The most advanced usage is to create a custom or a standard `net.Listener` and pass that to `app.Run` - -```go -// Using a custom net.Listener -l, err := net.Listen("tcp4", ":8080") -if err != nil { - panic(err) -} -app.Run(iris.Listener(l)) -``` - -A more complete example, using the unix-only socket files feature - -```go -package main - -import ( - "os" - "net" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - // UNIX socket - if errOs := os.Remove(socketFile); errOs != nil && !os.IsNotExist(errOs) { - app.Logger().Fatal(errOs) - } - - l, err := net.Listen("unix", socketFile) - - if err != nil { - app.Logger().Fatal(err) - } - - if err = os.Chmod(socketFile, mode); err != nil { - app.Logger().Fatal(err) - } - - app.Run(iris.Listener(l)) -} -``` - -### HTTP/2 and Secure - -If you have signed file keys you can use the `iris.TLS` to serve `https` based on those certification keys - -```go -// TLS using files -app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key")) -``` - -The method you should use when your app is ready for **production** is the `iris.AutoTLS` which starts a secure server with automated certifications provided by https://letsencrypt.org for **free** - -```go -// Automatic TLS -app.Run(iris.AutoTLS(":443", "example.com", "admin@example.com")) -``` - -### Any `iris.Runner` - -There may be times that you want something very special to listen on, which is not a type of `net.Listener`. You are able to do that by `iris.Raw`, but you're responsible of that method - -```go -// Using any func() error, -// the responsibility of starting up a listener is up to you with this way, -// for the sake of simplicity we will use the -// ListenAndServe function of the `net/http` package. -app.Run(iris.Raw(&http.Server{Addr:":8080"}).ListenAndServe) -``` - -## Host configurators - -All the above forms of listening are accepting a last, variadic argument of `func(*iris.Supervisor)`. This is used to add configurators for that specific host you passed via those functions. - -For example let's say that we want to add a callback which is fired when -the server is shutdown - -```go -app.Run(iris.Addr(":8080", func(h *iris.Supervisor) { - h.RegisterOnShutdown(func() { - println("server terminated") - }) -})) -``` - -You can even do that before `app.Run` method, but the difference is that -these host configurators will be executed to all hosts that you may use to serve your web app (via `app.NewHost` we'll see that in a minute) - -```go -app := iris.New() -app.ConfigureHost(func(h *iris.Supervisor) { - h.RegisterOnShutdown(func() { - println("server terminated") - }) -}) -app.Listen(":8080") -``` - -Access to all hosts that serve your application can be provided by -the `Application#Hosts` field, after the `Run` method. - -But the most common scenario is that you may need access to the host before the `app.Run` method, -there are two ways of gain access to the host supervisor, read below. - -We have already saw how to configure all application's hosts by second argument of `app.Run` or `app.ConfigureHost`. There is one more way which suits better for simple scenarios and that is to use the `app.NewHost` to create a new host -and use one of its `Serve` or `Listen` functions -to start the application via the `iris#Raw` Runner. - -Note that this way needs an extra import of the `net/http` package. - -Example Code: - -```go -h := app.NewHost(&http.Server{Addr:":8080"}) -h.RegisterOnShutdown(func(){ - println("server terminated") -}) - -app.Run(iris.Raw(h.ListenAndServe)) -``` - -## Multi hosts - -You can serve your Iris web app using more than one server, the `iris.Router` is compatible with the `net/http/Handler` function therefore, as you can understand, it can be used to be adapted at any `net/http` server, however there is an easier way, by using the `app.NewHost` which is also copying all the host configurators and it closes all the hosts attached to the particular web app on `app.Shutdown`. - -```go -app := iris.New() -app.Get("/", indexHandler) - -// run in different goroutine in order to not block the main "goroutine". -go app.Listen(":8080") -// start a second server which is listening on tcp 0.0.0.0:9090, -// without "go" keyword because we want to block at the last server-run. -app.NewHost(&http.Server{Addr:":9090"}).ListenAndServe() -``` - -## Shutdown (Gracefully) - -Let's continue by learning how to catch CONTROL+C/COMMAND+C or unix kill command and shutdown the server gracefully. - -> Gracefully Shutdown on CONTROL+C/COMMAND+C or when kill command sent is ENABLED BY-DEFAULT. - -In order to manually manage what to do when app is interrupted, -we have to disable the default behavior with the option `WithoutInterruptHandler` -and register a new interrupt handler (globally, across all possible hosts). - - -Example code: - -```go -package main - -import ( - "context" - "time" - - "github.com/kataras/iris/v12" -) - - -func main() { - app := iris.New() - - iris.RegisterOnInterrupt(func() { - timeout := 10 * time.Second - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - // close all hosts - app.Shutdown(ctx) - }) - - app.Get("/", func(ctx iris.Context) { - ctx.HTML("

hi, I just exist in order to see if the server is closed

") - }) - - app.Listen(":8080", iris.WithoutInterruptHandler) -} -``` diff --git a/_examples/http-server/custom-httpserver/easy-way/main.go b/_examples/http-server/custom-httpserver/easy-way/main.go deleted file mode 100644 index b6f71e8aa9..0000000000 --- a/_examples/http-server/custom-httpserver/easy-way/main.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - ctx.Writef("Hello from the server") - }) - - app.Get("/mypath", func(ctx iris.Context) { - ctx.Writef("Hello from %s", ctx.Path()) - }) - - // Any custom fields here. Handler and ErrorLog are set to the server automatically - srv := &http.Server{Addr: ":8080"} - - // http://localhost:8080/ - // http://localhost:8080/mypath - app.Run(iris.Server(srv)) // same as app.Listen(":8080") - - // More: - // see "multi" if you need to use more than one server at the same app. - // - // for a custom listener use: iris.Listener(net.Listener) or - // iris.TLS(cert,key) or iris.AutoTLS(), see "custom-listener" example for those. -} diff --git a/_examples/http-server/custom-httpserver/multi/main.go b/_examples/http-server/custom-httpserver/multi/main.go deleted file mode 100644 index 083e8e6cfa..0000000000 --- a/_examples/http-server/custom-httpserver/multi/main.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - ctx.Writef("Hello from the server") - }) - - app.Get("/mypath", func(ctx iris.Context) { - ctx.Writef("Hello from %s", ctx.Path()) - }) - - // Note: It's not needed if the first action is "go app.Run". - if err := app.Build(); err != nil { - panic(err) - } - - // start a secondary server listening on localhost:9090. - // use "go" keyword for Listen functions if you need to use more than one server at the same app. - // - // http://localhost:9090/ - // http://localhost:9090/mypath - srv1 := &http.Server{Addr: ":9090", Handler: app} - go srv1.ListenAndServe() - println("Start a server listening on http://localhost:9090") - - // start a "second-secondary" server listening on localhost:5050. - // - // http://localhost:5050/ - // http://localhost:5050/mypath - srv2 := &http.Server{Addr: ":5050", Handler: app} - go srv2.ListenAndServe() - println("Start a server listening on http://localhost:5050") - - // Note: app.Run is totally optional, we have already built the app with app.Build, - // you can just make a new http.Server instead. - // http://localhost:8080/ - // http://localhost:8080/mypath - app.Listen(":8080") // Block here. -} diff --git a/_examples/http-server/custom-httpserver/std-way/main.go b/_examples/http-server/custom-httpserver/std-way/main.go deleted file mode 100644 index b4817d2d53..0000000000 --- a/_examples/http-server/custom-httpserver/std-way/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "net/http" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - ctx.Writef("Hello from the server") - }) - - app.Get("/mypath", func(ctx iris.Context) { - ctx.Writef("Hello from %s", ctx.Path()) - }) - - // call .Build before use the 'app' as a http.Handler on a custom http.Server - app.Build() - - // create our custom server and assign the Handler/Router - srv := &http.Server{Handler: app, Addr: ":8080"} // you have to set Handler:app and Addr, see "iris-way" which does this automatically. - // http://localhost:8080/ - // http://localhost:8080/mypath - println("Start a server listening on http://localhost:8080") - srv.ListenAndServe() // same as app.Listen(":8080") - - // Notes: - // Banner is not shown at all. Same for the Interrupt Handler, even if app's configuration allows them. - // - // `.Run` is the only one function that cares about those three. - - // More: - // see "multi" if you need to use more than one server at the same app. - // - // for a custom listener use: iris.Listener(net.Listener) or - // iris.TLS(cert,key) or iris.AutoTLS(), see "custom-listener" example for those. -} diff --git a/_examples/http-server/custom-listener/main.go b/_examples/http-server/custom-listener/main.go deleted file mode 100644 index eb156fd4f4..0000000000 --- a/_examples/http-server/custom-listener/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "net" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - ctx.Writef("Hello from the server") - }) - - app.Get("/mypath", func(ctx iris.Context) { - ctx.Writef("Hello from %s", ctx.Path()) - }) - - // create any custom tcp listener, unix sock file or tls tcp listener. - l, err := net.Listen("tcp4", ":8080") - if err != nil { - panic(err) - } - - // use of the custom listener - app.Run(iris.Listener(l)) -} diff --git a/_examples/http-server/graceful-shutdown/custom-notifier/main.go b/_examples/http-server/graceful-shutdown/custom-notifier/main.go deleted file mode 100644 index 3984aefb4f..0000000000 --- a/_examples/http-server/graceful-shutdown/custom-notifier/main.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - stdContext "context" - "os" - "os/signal" - "syscall" - "time" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - ctx.HTML("

hi, I just exist in order to see if the server is closed

") - }) - - go func() { - ch := make(chan os.Signal, 1) - signal.Notify(ch, - // kill -SIGINT XXXX or Ctrl+c - os.Interrupt, - syscall.SIGINT, // register that too, it should be ok - // os.Kill is equivalent with the syscall.Kill - os.Kill, - syscall.SIGKILL, // register that too, it should be ok - // kill -SIGTERM XXXX - syscall.SIGTERM, - ) - select { - case <-ch: - println("shutdown...") - - timeout := 10 * time.Second - ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout) - defer cancel() - app.Shutdown(ctx) - } - }() - - // Start the server and disable the default interrupt handler in order to - // handle it clear and simple by our own, without any issues. - app.Listen(":8080", iris.WithoutInterruptHandler) -} diff --git a/_examples/http-server/graceful-shutdown/default-notifier/main.go b/_examples/http-server/graceful-shutdown/default-notifier/main.go deleted file mode 100644 index 06acf4abac..0000000000 --- a/_examples/http-server/graceful-shutdown/default-notifier/main.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - stdContext "context" - "time" - - "github.com/kataras/iris/v12" -) - -// Before continue: -// -// Gracefully Shutdown on control+C/command+C or when kill command sent is ENABLED BY-DEFAULT. -// -// In order to manually manage what to do when app is interrupted, -// We have to disable the default behavior with the option `WithoutInterruptHandler` -// and register a new interrupt handler (globally, across all possible hosts). -func main() { - app := iris.New() - - iris.RegisterOnInterrupt(func() { - timeout := 10 * time.Second - ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout) - defer cancel() - // close all hosts - app.Shutdown(ctx) - }) - - app.Get("/", func(ctx iris.Context) { - ctx.HTML("

hi, I just exist in order to see if the server is closed

") - }) - - // http://localhost:8080 - app.Listen(":8080", iris.WithoutInterruptHandler) -} diff --git a/_examples/http-server/http3-quic/go.mod b/_examples/http-server/http3-quic/go.mod deleted file mode 100644 index bc0e2b953c..0000000000 --- a/_examples/http-server/http3-quic/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module github.com/kataras/iris/_examples/http-server/http3-quic - -go 1.15 - -require ( - github.com/prometheus/client_golang v1.0.0 -) diff --git a/_examples/http-server/http3-quic/localhost.cert b/_examples/http-server/http3-quic/localhost.cert deleted file mode 100644 index f440f3f194..0000000000 --- a/_examples/http-server/http3-quic/localhost.cert +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC+zCCAeOgAwIBAgIJAI5gi8BzcdQgMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV -BAMMCWxvY2FsaG9zdDAeFw0xOTA3MDkxMjExNDBaFw0yOTA3MDYxMjExNDBaMBQx -EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAM1eSGwoYznwYtt+/hrv9o4FYxJFlxIMX6WN3c2y3rr8uOwExkz2RuU9SzgF -qn0ctP1DoIKWNO0/L5j5Bjy2do0k8wHYPbTqb9zG64NGZj1lhkHgYXWyCD9U41DX -V1DiJ2JiCRBadowFRRf3/KIPf3xnrCBSCoQwdfIeJJtAF9El2/TnTrGq9N98FJqR -dCNyi+zY/iuymcA3aDOyYNjxSiuV//7ONEql5dxvRlhkjCHgrQ/rIbH/lSFAS+NG -H/6ksEBX2+Q1LlQBaFiGeEnjVpCymrRfADw7bKyqMav39Nndw4UZ1r5MSG20YtXM -dE4lA6VfAKzIZs2n87WF7OO8Y2sCAwEAAaNQME4wHQYDVR0OBBYEFKYItamcYz4Z -NiDy3I2zflU4A7ywMB8GA1UdIwQYMBaAFKYItamcYz4ZNiDy3I2zflU4A7ywMAwG -A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBACSoSrEArlgPJ1ftlSMkThUR -atTqJ/yB0rSyRxsct0C04qX880VP7avnKc0UhaDruXRjdAVn4X8KI+j6azQSKT40 -KRSVBinnonE0D4DBMCUVDFtkBW3FZJXAYyIYdF/6J3Khn/ksm7VDcVxYI1rjg87B -U6aJytOkoGA2WGQOB1L0HtnTsarg/SKP/LSDUFT+XK6zTE7uogAUrpbwlpIaxc+8 -3jXvgxEdPj9Rq9Nt8/zjCkCGB2EusPPnqxcbqZb5WcGPCIlg3ChKq7vpaQld6KqG -70jT7BZ2fqWSVJ5szRoS8WpKy1SZEx/+AA7VojMzkkw4RLb66zr1v7029b51ol4= ------END CERTIFICATE----- diff --git a/_examples/http-server/http3-quic/localhost.key b/_examples/http-server/http3-quic/localhost.key deleted file mode 100644 index ee1cdae6c6..0000000000 --- a/_examples/http-server/http3-quic/localhost.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAzV5IbChjOfBi237+Gu/2jgVjEkWXEgxfpY3dzbLeuvy47ATG -TPZG5T1LOAWqfRy0/UOggpY07T8vmPkGPLZ2jSTzAdg9tOpv3Mbrg0ZmPWWGQeBh -dbIIP1TjUNdXUOInYmIJEFp2jAVFF/f8og9/fGesIFIKhDB18h4km0AX0SXb9OdO -sar033wUmpF0I3KL7Nj+K7KZwDdoM7Jg2PFKK5X//s40SqXl3G9GWGSMIeCtD+sh -sf+VIUBL40Yf/qSwQFfb5DUuVAFoWIZ4SeNWkLKatF8APDtsrKoxq/f02d3DhRnW -vkxIbbRi1cx0TiUDpV8ArMhmzafztYXs47xjawIDAQABAoIBADvaKok7DBAquuT1 -keEP5m9lqoX8uhaMfKOnQOleJAOi+9HtYk2zyN2ui2l8XT+xSh41w2XLmQk7zQds -LCEtnEduaVQ0TWeYm5lgb+sGbW2fVQ2F82F1zWmHt+grmkr8XjYSFEor0zjjoEtn -/rzMf38mR8fzoRT9eqJhnpGQkGBnfo0SyDKIDu9yYFM7yJ5s4KOTVsMGfavjQYgJ -ssQm0KQTo8HbYHieS6drEYFRwAgT8U1NFoVq24yU+Voyy7CR5rjfOsO9gSyStVSH -nTkePmSIcpeQBfpfT3jh+STSS12eqhFCx1SqAUptUehQpOJhnWAyeSGjVLlrHqDR -bCtSWCECgYEA62J2AC8bRQ8omoEOc5h1/kwPTcLYjhOCxwwU8ky7qve173MYZUeT -dxWZx3haahccPyUKtsiGBdKYyYKSOIJMSMmwkG4uy6r5nhwNV+btEIL6Npj+XKMe -PaATA4gBLRQNwcbUlZWLYc0Y6CXFnPA+atEa7EOEBfdgUqHOym1HsAUCgYEA31rU -GPyv8R7Z1UXmxu70M8RwxKS4XhlP7RRg3Zuuqx6WWX4M85Np3wS4UWgQjGKICwoM -D4XKZQOya5p+v7a1RUZt/OD6eJ0TjypW5fBmK2yvBUQkQCsVcFjBL3F+yv2Yk1sh -KlPky4wXpDcmWXGmesgmyhpKIwL+qXEAJEaL0K8CgYABWqKlI6A7iHfKU726ioD7 -QoLABsPqJVCWRoqETk6yEBS62OWmB4Bgqf4leJrEi3d9IYBrRsIGnIyGdDrVGmLH -9GkQm6GnSEeBUlX9UHXCp4467CxiagnNfvM9DPY8xSXDHJqydZbErEJda4I0gelK -AgPuogDLa/3g289tuK015QKBgQDKcj9Qrqiiur3jC8rTgX8i9OjptAvQbsz9LL1n -4FZ/j+fjEdeXZ4RMurB+SP7G4ABDUUYBQ9lhmeo8kfpUtryzH9VNonYkoOs7lrrR -DAbvUUGKWmspJmP2QtxHrm2ofBexaKY1AXmd7Ur4c2x1IggtvgE6qn2MIojE+EGS -n8bWzQKBgQClc/j4GYNNMUYOknxXMH/ec8PBDUtn098YuFL+s7DmRHimtkkjRo1A -BtV7F8KpLruWohxXWy4QZ6HsAO255gIJ8DCbEAFCj96EHNx8KADSm3qbslu2fIB0 -zCsVaETGNAjV/d5hAEdYmgynCY49gNXV55ehV1UqzFoEfZVM9j5hUg== ------END RSA PRIVATE KEY----- diff --git a/_examples/http-server/http3-quic/main.go b/_examples/http-server/http3-quic/main.go deleted file mode 100644 index 54eebfbce4..0000000000 --- a/_examples/http-server/http3-quic/main.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - - "github.com/lucas-clemente/quic-go/http3" -) - -/* - $ go get -u github.com/lucas-clemente/quic-go/... - # or if you're using GO MODULES: - $ go get github.com/lucas-clemente/quic-go@master -*/ - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - ctx.Writef("Hello from Index") - }) - - // app.Configure(iris.WithOptimizations, or any other core config here) - // app.Build() - // http3.ListenAndServe(":443", "./localhost.cert", "./localhost.key", app) - // OR: - app.Run(iris.Raw(func() error { - return http3.ListenAndServe(":443", "./localhost.cert", "./localhost.key", app) - })) -} diff --git a/_examples/http-server/iris-configurator-and-host-configurator/main.go b/_examples/http-server/iris-configurator-and-host-configurator/main.go deleted file mode 100644 index ddece844bb..0000000000 --- a/_examples/http-server/iris-configurator-and-host-configurator/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.ConfigureHost(func(host *iris.Supervisor) { // <- HERE: IMPORTANT - // You can control the flow or defer something using some of the host's methods: - // host.RegisterOnError - // host.RegisterOnServe - host.RegisterOnShutdown(func() { - app.Logger().Infof("Application shutdown on signal") - }) - }) - - app.Get("/", func(ctx iris.Context) { - ctx.HTML("

Hello

\n") - }) - - app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) - - /* There are more easy ways to notify for global shutdown using the `iris.RegisterOnInterrupt` for default signal interrupt events. - You can even go it even further by looking at the: "graceful-shutdown" example. - */ -} diff --git a/_examples/http-server/listen-addr-public/main.go b/_examples/http-server/listen-addr-public/main.go deleted file mode 100644 index 1e17cd0ef9..0000000000 --- a/_examples/http-server/listen-addr-public/main.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - ctx.HTML("

Hello World!

") - - // Will print the ngrok public domain - // that your app is using to be served online. - ctx.Writef("From: %s", - ctx.Application().ConfigurationReadOnly().GetVHost()) - }) - - app.Listen(":8080", iris.WithTunneling, iris.WithLogLevel("debug")) - - /* The full configuration can be set as: - app.Listen(":8080", iris.WithConfiguration( - iris.Configuration{ - Tunneling: iris.TunnelingConfiguration{ - AuthToken: "my-ngrok-auth-client-token", - Bin: "/bin/path/for/ngrok", - Region: "eu", - WebInterface: "127.0.0.1:4040", - Tunnels: []iris.Tunnel{ - { - Name: "MyApp", - Addr: ":8080", - }, - }, - }, - })) - */ -} diff --git a/_examples/http-server/listen-addr/main.go b/_examples/http-server/listen-addr/main.go deleted file mode 100644 index 551fded82d..0000000000 --- a/_examples/http-server/listen-addr/main.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - ctx.HTML("

Hello World!

") - }) - - // http://localhost:8080 - // Identical to: app.Run(iris.Addr(":8080")) - app.Listen(":8080") -} diff --git a/_examples/http-server/listen-addr/omit-server-errors/main.go b/_examples/http-server/listen-addr/omit-server-errors/main.go deleted file mode 100644 index d2a62a6e79..0000000000 --- a/_examples/http-server/listen-addr/omit-server-errors/main.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - ctx.HTML("

Hello World!

") - }) - - err := app.Listen(":8080", iris.WithoutServerError(iris.ErrServerClosed)) - if err != nil { - // do something - } - // same as: - // err := app.Listen(":8080") - // import "errors" - // if errors.Is(err, iris.ErrServerClosed) { - // [...] - // } -} diff --git a/_examples/http-server/listen-addr/omit-server-errors/main_test.go b/_examples/http-server/listen-addr/omit-server-errors/main_test.go deleted file mode 100644 index 89bc6bf9db..0000000000 --- a/_examples/http-server/listen-addr/omit-server-errors/main_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package main - -import ( - "bytes" - stdContext "context" - "strings" - "testing" - "time" - - "github.com/kataras/iris/v12" -) - -func logger(app *iris.Application) *bytes.Buffer { - buf := &bytes.Buffer{} - - app.Logger().SetOutput(buf) - - // disable the "Now running at...." in order to have a clean log of the error. - // we could attach that on `Run` but better to keep things simple here. - app.Configure(iris.WithoutStartupLog) - return buf -} - -func TestListenAddr(t *testing.T) { - app := iris.New() - // we keep the logger running as well but in a controlled way. - log := logger(app) - - // close the server at 3-6 seconds - go func() { - time.Sleep(3 * time.Second) - ctx, cancel := stdContext.WithTimeout(stdContext.TODO(), 3*time.Second) - defer cancel() - app.Shutdown(ctx) - }() - - err := app.Listen(":9829") - // in this case the error should be logged and return as well. - if err != iris.ErrServerClosed { - t.Fatalf("expecting err to be `iris.ErrServerClosed` but got: %v", err) - } - - expectedMessage := iris.ErrServerClosed.Error() - - if got := log.String(); !strings.Contains(got, expectedMessage) { - t.Fatalf("expecting to log to contains the:\n'%s'\ninstead of:\n'%s'", expectedMessage, got) - } -} - -func TestListenAddrWithoutServerErr(t *testing.T) { - app := iris.New() - // we keep the logger running as well but in a controlled way. - log := logger(app) - - // close the server at 3-6 seconds - go func() { - time.Sleep(3 * time.Second) - ctx, cancel := stdContext.WithTimeout(stdContext.TODO(), 3*time.Second) - defer cancel() - app.Shutdown(ctx) - }() - - // we disable the ErrServerClosed, so the error should be nil when server is closed by `app.Shutdown` - // or by an external issue. - err := app.Listen(":9827", iris.WithoutServerError(iris.ErrServerClosed)) - if err != nil { - t.Fatalf("expecting err to be nil but got: %v", err) - } - - if got := log.String(); got != "" { - t.Fatalf("expecting to log nothing but logged: '%s'", got) - } -} diff --git a/_examples/http-server/listen-letsencrypt/main.go b/_examples/http-server/listen-letsencrypt/main.go deleted file mode 100644 index d49be671a4..0000000000 --- a/_examples/http-server/listen-letsencrypt/main.go +++ /dev/null @@ -1,42 +0,0 @@ -// Package main provide one-line integration with letsencrypt.org -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - ctx.Writef("Hello from SECURE SERVER!") - }) - - app.Get("/test2", func(ctx iris.Context) { - ctx.Writef("Welcome to secure server from /test2!") - }) - - app.Get("/redirect", func(ctx iris.Context) { - ctx.Redirect("/test2") - }) - - // NOTE: This will not work on domains like this, - // use real whitelisted domain(or domains split by whitespaces) - // and a non-public e-mail instead or edit your hosts file. - app.Run(iris.AutoTLS(":443", "example.com", "mail@example.com")) - - // Note: to disable automatic "http://" to "https://" redirections pass - // the `iris.AutoTLSNoRedirect` host configurator to AutoTLS function, example: - /* - var fallbackServer = func(acme func(http.Handler) http.Handler) *http.Server { - // Use any http.Server and Handler, as long as it's wrapped by `acme` one. - // In that case we share the application through non-tls users too: - srv := &http.Server{Handler: acme(app)} - go srv.ListenAndServe() - return srv - } - - app.Run(iris.AutoTLS(":443", "example.com", "mail@example.com", - iris.AutoTLSNoRedirect(fallbackServer))) - */ -} diff --git a/_examples/http-server/listen-tls/main.go b/_examples/http-server/listen-tls/main.go deleted file mode 100644 index 50d56540db..0000000000 --- a/_examples/http-server/listen-tls/main.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - ctx.Writef("Hello from the SECURE server") - }) - - app.Get("/mypath", func(ctx iris.Context) { - ctx.Writef("Hello from the SECURE server on path /mypath") - }) - - // Start the server (HTTPS) on port 443, - // and a secondary of (HTTP) on port :80 which redirects requests to their HTTPS version. - // This is a blocking func. - app.Run(iris.TLS("127.0.0.1:443", "mycert.crt", "mykey.key")) - - // Note: to disable automatic "http://" to "https://" redirections pass the `iris.TLSNoRedirect` - // host configurator to TLS function, example: - // - // app.Run(iris.TLS("127.0.0.1:443", "mycert.crt", "mykey.key", iris.TLSNoRedirect)) -} diff --git a/_examples/http-server/listen-tls/mycert.crt b/_examples/http-server/listen-tls/mycert.crt deleted file mode 100644 index 9db93b09df..0000000000 --- a/_examples/http-server/listen-tls/mycert.crt +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFazCCA1OgAwIBAgIUfwMd9auWixp19UnXOmyxJ9Jkv7IwDQYJKoZIhvcNAQEL -BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM -GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA2MjUwOTUxNDdaFw0yMTA2 -MjUwOTUxNDdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw -HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQDlVGyGAQ9uyfNbwZyrtYOSjLpxf5NpNToh2OzU7gy2 -OexBji5lmWBQ3oYDG+FjAkbHORPzOMNpeMwje+IjGZBw8x6E+8WoGdSzbrEZ6pUV -wKJGKEuDlx6g6HEmtv3ZwgGe20gvPjjW+oCO888dwK/mbIHrHTq4nO3o0gAdAJwu -amn9BlHU5O4RW7BQ4tLF+j/fBCACWRG1NHXA0AT8eg544GyCdyteAH11oCDsHS8/ -DAPsM6t+tZrMCIt9+9dzPdVoOmQNaMMrcz8eJohddRTK6zHe9ixZTt/soayOF7OS -QQeekbr3HPYhD450zRVplLMHx7wnph/+O+Po6bqDnUzdnkqAAwwymQapHMuHXZKN -rhdfKau3rVo1GeXLIRgeWLUoxFSm4TYshrgt+0AidLRH+dCY7MS9Ngga/sAK3vID -gSF75mFgOhY+q7nvY9Ecao6TnoNNRY29hUat4y0VwSyysUy887vHr6lMK5CrAT/l -Ch8fuu20HUCoiLwMJvA6+wpivZkuiIvWY7bVGYsEYrrW+bCNN9wCGYTZEyX++os9 -v/38wdOqGUT00ewXkjIUFCWbrnxxSr98kF3w3wPf9K4Y40MNxeR90nyX4zjXGF1/ -91msUh+iivsz9mcN9DK83fgTyOsoVLX5cm/L2UBwMacsfjBbN4djOc5IuYMar/VN -GQIDAQABo1MwUTAdBgNVHQ4EFgQUtkf+yAvqgZC8f22iJny9hFEDolMwHwYDVR0j -BBgwFoAUtkf+yAvqgZC8f22iJny9hFEDolMwDwYDVR0TAQH/BAUwAwEB/zANBgkq -hkiG9w0BAQsFAAOCAgEAE2QasBVru618rxupyJgEHw6r4iv7sz1Afz3Q5qJ4oSA9 -xVsrVCjr3iHRFSw8Rf670E8Ffk/JjzS65mHw6zeZj/ANBKQWLjRlqzYXeetq5HzG -SIgaG7p1RFvvzz3+leFGzjinZ6sKbfB4OB72o2YN+fO8DsDxgGKll0W4KAazizSe -HY9Pgu437tWnwF16rFO3IL47n5HzYlRoGIPOpzFoNX5+fyn9GlnKEtONF2QBKTjY -rdjvqFRByDiC74d8z/Yx8IiDRn1mTcG90JLR9+c6M7fruha9Y/rJfw+4AhVh5ZDz -Bl9rGPjwEs5zwutYvVAJzs7AVcighYP1lHKoJ7DxBDQeyBsYlUNk2l6bmZgLgGUZ -+2OyWlqc/jD2GdDsIaZ4i7QqhTI/6aYZIf5zUkblKV1aMSaDulKxRv//OwW28Jax -9EEoV7VaFb3sOkB/tZGhusXeQVtdrhahT3KkZLNwmNXoXWKJ5LjeUlFWJyV6JbDe -y/PIWWCwWqyuFCSZS+Cg3RDgAzfSxkI8uVZ+IKKJS3UluDX45lxXtbRrvTQ+oDrA -6ga5c1Vz9C4kn1K5yW4d7QIvg6vPiy7gvl+//sz9oxUM3yswInDBY0HKLgT0Uq9b -YzLDh2RSaHsgHMPy2BKqR+q2N+lpg7inAWuJM1Huq6eHFqhiyQkzsfscBd1Dpm8= ------END CERTIFICATE----- diff --git a/_examples/http-server/listen-tls/mykey.key b/_examples/http-server/listen-tls/mykey.key deleted file mode 100644 index 39f7b3af50..0000000000 --- a/_examples/http-server/listen-tls/mykey.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDlVGyGAQ9uyfNb -wZyrtYOSjLpxf5NpNToh2OzU7gy2OexBji5lmWBQ3oYDG+FjAkbHORPzOMNpeMwj -e+IjGZBw8x6E+8WoGdSzbrEZ6pUVwKJGKEuDlx6g6HEmtv3ZwgGe20gvPjjW+oCO -888dwK/mbIHrHTq4nO3o0gAdAJwuamn9BlHU5O4RW7BQ4tLF+j/fBCACWRG1NHXA -0AT8eg544GyCdyteAH11oCDsHS8/DAPsM6t+tZrMCIt9+9dzPdVoOmQNaMMrcz8e -JohddRTK6zHe9ixZTt/soayOF7OSQQeekbr3HPYhD450zRVplLMHx7wnph/+O+Po -6bqDnUzdnkqAAwwymQapHMuHXZKNrhdfKau3rVo1GeXLIRgeWLUoxFSm4TYshrgt -+0AidLRH+dCY7MS9Ngga/sAK3vIDgSF75mFgOhY+q7nvY9Ecao6TnoNNRY29hUat -4y0VwSyysUy887vHr6lMK5CrAT/lCh8fuu20HUCoiLwMJvA6+wpivZkuiIvWY7bV -GYsEYrrW+bCNN9wCGYTZEyX++os9v/38wdOqGUT00ewXkjIUFCWbrnxxSr98kF3w -3wPf9K4Y40MNxeR90nyX4zjXGF1/91msUh+iivsz9mcN9DK83fgTyOsoVLX5cm/L -2UBwMacsfjBbN4djOc5IuYMar/VNGQIDAQABAoICAQCtWx1SSxjkcerxsLEDKApW -zOTfiUXgoOjZz0ZwS6b2VWDfyWAPU1r4ps39KaU+F+lzDhWjpYQqhbMjG7G9QMTs -bQvkEQLAaQ5duU5NPgQG1oCUsj8rMSBpGGz4jBnm834QHMk7VTjYYbKu3WTyo8cU -U2/+UDEkfxRlC+IkCmMFv1FxgMZ5PbktC/eDnYMhP2Pq7Q5ZWAVHymk9IMK0LHwm -Kdg842K4A3zTXwGkGwetDCMm+YQpG5TxqX/w82BRcCuTR5h8fnYSsWLEIvKwWyIl -ppcjaUnrFPG2yhxLqWUIKPpehuEjjhQMt9rDNoh6MHsJZZY5Dp5eq91EIvLoLQ99 -hXBmD4P8LDop4r0jniPZJi/ACsaD0jBooA4525+Kouq7RP28Jp/pek7lVOOcBgRv -D3zyESbKfqoaOfyfQ2ff4sILnTAr4V2nq3ekphGEYJrWN0ZoADcLdnr1cZ8L+VBI -o/4mi5/3HID/UEDliHSa97hxxGBEqTto0ZuXuNwfwx5ho33uVT6zNwRgiJ62Bgu3 -Fhk/wVGuZxWvb1KHUNInG9cvsslhO4Vu9wJvYj91BnRq36rsyKKid5DrU+PNgmog -lw3IXQpTojyRCYPuG9TKqEZ6b+so7GTKhBOjiwaupMOletVRGSAdbE81VN6HtxNW -aj39+FnxzMAlsieib+PBAQKCAQEA+t1fOYSaZBo7pZUmo2S0zulUEJjrYRGKJlWJ -4psWSwFu/7/3UL4q0RBQaSRew9u/YSpaNlBYfcpnFVOjiLwHq5Hx46Eq0BuKsNlJ -1/qxw9qjHqcrOre6K4/7NaWLPuM9fEmV+3MhFVXgv+WC5BHOowRTlOG30vIcC1J2 -L5xsBUsxDDY13cD1bLKRmFcyMFM8y7wMZmo7H/WfVmyoPKQaC43pTcmIXH0Jr2Ws -Wsfh18mhjtamaOPEFx5K0x4d0PI8tW5ouiUUkVIDaue27XfS969qEChv768/44eX -WeqcekaG9jv2noMClt79rYd3Lne9HkgY6IT9FT+JqXfu+KYwuQKCAQEA6gYzUsGB -9GQO8DE8AYn7JwNOtg1X4zKakXiGxH+nuZb7wJjAeGdYqTHySxPBXg0A2nDwoyz5 -4sAdLAr3FZoIvTzo7M5KIKFDzfyDmQDavhroH1mBAEiqKGNniP+RND3nWBBqDK1R -qcqbhI3Kj5Ycany6a4nP+hZRBIyT9sfJ0S0YruSY8IGXgDwhlJrZ7bsWMZylrgD/ -1qnPL0KqVBY8YR8msRj88h72IlD5o0kwvisOIvyhA0YgwGBb6lg7A+DifiF03ZlS -2yELbIkKDVr+p3jC7MBh4B+OJY68AMl6wVjAaDM1AZnpjKE5YmZg5+Ks5823zILo -PrSB9hn0+DIPYQKCAQEAh9x+JuNmzhHa/dkiHNl8hpadHYQD7gUWwZ4P1/bQAv0a -xU2MvmDPRXxFYDv/SqlnI1NRmhq3YiDM5SLv7SyQJt4al4IAcsaHvTFgqaSuw3hU -YVR9uAYqwE7w6OPn3r4o3Xfoz05Ru4FP//1nfucZ9vVv4rC/4nGWuJcHRM+9PLy1 -KnztfVR0VlL7QPrwRnW99kS4nnqn3K4khiTAlF73cAyCLsuXmydoqGIzDtMzv68G -XRpo82NvHmoccevcj/2w3T2XYECWvAEjsrEdQ8xiKBwLIAcWYEOUIUCcumiyKBKs -IwzkioI/U8AeuO0lobfdZ1n6i2sCuZA4mNxIQseWmQKCAQEA5YkfXdQeuq5JWJ1x -1bCYfjNoSHfd9CH2KSimRqVOxWGpm8Y3QeFbvNgYZjsCNlVauOZ9oA7FKfp0onY+ -0xk56SKM83eCjW6fKrK6AKAt7LhHZDhNpxGek+6r5luE+FCfUGkJG1YD+x2WW/UW -8K6zQF8GGeQZ8Zlh7axUlIBxGpG43BGrUHpLNqPD7BXWGq6dnhufBYRFay8y34/r -sH3+yuPa92ki7/geQppZwCZRgLSKMRbIdoWaKhZZEQlpGOzCOiRmk9OGyRcoNVRU -X7UYgPqZdc1cMo/AxGWzULJNjMaYMZvIKcHkqOKZfkIcWlSictn7pMPhN1+k+NWM -yMORAQKCAQAyXl02h/c2ihx6cjKlnNeDr2ZfzkoiAvFuKaoAR+KVvb9F9X7ZgKSi -wudZyelTglIVCYXeRmG09uX3rNGCzFrweRwgn6x/8DnN5pMRJVZOXFdgR+V9uKep -K6F7DYbPyggvLOAsezB+09i9lwxM+XdA2whVpL5NFR1rGfFglnE1EQHcEvNONkcv -0h8x9cNSptJyRDLiTIKI9EhonuzwzkGpvjULQE8MLbT8PbjoLFINcE9ZWhwtyw0V -XO32KE8iLKt3KzHz9CfTRCI3M7DwD752AC6zRr8ZS/HXzs+5WTkdVVEtRC7Abd3y -W2TzuSMYNDu876twbTVQJED3mwOAQ3J7 ------END PRIVATE KEY----- diff --git a/_examples/http-server/listen-unix/main.go b/_examples/http-server/listen-unix/main.go deleted file mode 100644 index 73096504be..0000000000 --- a/_examples/http-server/listen-unix/main.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/core/netutil" -) - -func main() { - app := iris.New() - - l, err := netutil.UNIX("/tmpl/srv.sock", 0666) // see its code to see how you can manually create a new file listener, it's easy. - if err != nil { - panic(err) - } - - app.Run(iris.Listener(l)) -} diff --git a/_examples/http-server/notify-on-shutdown/main.go b/_examples/http-server/notify-on-shutdown/main.go deleted file mode 100644 index b35a4818e4..0000000000 --- a/_examples/http-server/notify-on-shutdown/main.go +++ /dev/null @@ -1,59 +0,0 @@ -package main - -import ( - "context" - "time" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - ctx.HTML("

Hello, try to refresh the page after ~5 secs

") - }) - - app.Logger().Info("Wait 5 seconds and check your terminal again") - // simulate a shutdown action here... - go func() { - <-time.After(5 * time.Second) - timeout := 10 * time.Second - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - // close all hosts, this will notify the callback we had register - // inside the `configureHost` func. - app.Shutdown(ctx) - }() - - // app.ConfigureHost(configureHost) -> or pass "configureHost" as `app.Addr` argument, same result. - - // start the server as usual, the only difference is that - // we're adding a second (optional) function - // to configure the just-created host supervisor. - // - // http://localhost:8080 - // wait 10 seconds and check your terminal. - app.Run(iris.Addr(":8080", configureHost), iris.WithoutServerError(iris.ErrServerClosed)) - - time.Sleep(500 * time.Millisecond) // give time to the separate go routine(`onServerShutdown`) to finish. - - /* See - iris.RegisterOnInterrupt(callback) for global catch of the CTRL/CMD+C and OS events. - Look at the "graceful-shutdown" example for more. - */ -} - -func onServerShutdown() { - println("server is closed") -} - -func configureHost(su *iris.Supervisor) { - // here we have full access to the host that will be created - // inside the `app.Run` function or `NewHost`. - // - // we're registering a shutdown "event" callback here: - su.RegisterOnShutdown(onServerShutdown) - // su.RegisterOnError - // su.RegisterOnServe -} diff --git a/_examples/http-server/socket-sharding/main.go b/_examples/http-server/socket-sharding/main.go deleted file mode 100644 index bd5d7e4a44..0000000000 --- a/_examples/http-server/socket-sharding/main.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12" -) - -func main() { - startup := time.Now() - - app := iris.New() - app.Get("/", func(ctx iris.Context) { - s := startup.Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat()) - ctx.Writef("This server started at: %s\n", s) - }) - - // This option allows linear scaling server performance on multi-CPU servers. - // See https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/ for details. - app.Listen(":8080", iris.WithSocketSharding) -} diff --git a/_examples/i18n/hosts b/_examples/i18n/hosts deleted file mode 100644 index fb394e4b1f..0000000000 --- a/_examples/i18n/hosts +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 1993-2009 Microsoft Corp. -# -# This is a sample HOSTS file used by Microsoft TCP/IP for Windows. -# -# This file contains the mappings of IP addresses to host names. Each -# entry should be kept on an individual line. The IP address should -# be placed in the first column followed by the corresponding host name. -# The IP address and the host name should be separated by at least one -# space. -# -# Additionally, comments (such as these) may be inserted on individual -# lines or following the machine name denoted by a '#' symbol. -# -# For example: -# -# 102.54.94.97 rhino.acme.com # source server -# 38.25.63.10 x.acme.com # x client host - -# localhost name resolution is handled within DNS itself. -# 127.0.0.1 localhost -# ::1 localhost -127.0.0.1 mydomain.com -127.0.0.1 en.mydomain.com -127.0.0.1 el.mydomain.com -127.0.0.1 el-gr.mydomain.com -127.0.0.1 zh.mydomain.com \ No newline at end of file diff --git a/_examples/i18n/locales/el-GR/locale_el-GR.ini b/_examples/i18n/locales/el-GR/locale_el-GR.ini deleted file mode 100644 index a99e7fcc12..0000000000 --- a/_examples/i18n/locales/el-GR/locale_el-GR.ini +++ /dev/null @@ -1 +0,0 @@ -hi = γεια, %s \ No newline at end of file diff --git a/_examples/i18n/locales/el-GR/locale_multi_first_el-GR.ini b/_examples/i18n/locales/el-GR/locale_multi_first_el-GR.ini deleted file mode 100644 index cf043c864f..0000000000 --- a/_examples/i18n/locales/el-GR/locale_multi_first_el-GR.ini +++ /dev/null @@ -1 +0,0 @@ -key1 = αυτό είναι μια τιμή από το πρώτο αρχείο: locale_multi_first \ No newline at end of file diff --git a/_examples/i18n/locales/el-GR/locale_multi_second_el-GR.ini b/_examples/i18n/locales/el-GR/locale_multi_second_el-GR.ini deleted file mode 100644 index 6569d65b30..0000000000 --- a/_examples/i18n/locales/el-GR/locale_multi_second_el-GR.ini +++ /dev/null @@ -1 +0,0 @@ -key2 = αυτό είναι μια τιμή από το δεύτερο αρχείο μετάφρασης: locale_multi_second \ No newline at end of file diff --git a/_examples/i18n/locales/en-US/locale_en-US.ini b/_examples/i18n/locales/en-US/locale_en-US.ini deleted file mode 100644 index b2a394330c..0000000000 --- a/_examples/i18n/locales/en-US/locale_en-US.ini +++ /dev/null @@ -1 +0,0 @@ -hi = hello, %s \ No newline at end of file diff --git a/_examples/i18n/locales/en-US/locale_multi_first_en-US.ini b/_examples/i18n/locales/en-US/locale_multi_first_en-US.ini deleted file mode 100644 index 9e26482164..0000000000 --- a/_examples/i18n/locales/en-US/locale_multi_first_en-US.ini +++ /dev/null @@ -1 +0,0 @@ -key1 = this is a value from the first file: locale_multi_first \ No newline at end of file diff --git a/_examples/i18n/locales/en-US/locale_multi_second_en-US.ini b/_examples/i18n/locales/en-US/locale_multi_second_en-US.ini deleted file mode 100644 index 97826b69e1..0000000000 --- a/_examples/i18n/locales/en-US/locale_multi_second_en-US.ini +++ /dev/null @@ -1 +0,0 @@ -key2 = this is a value from the second file: locale_multi_second \ No newline at end of file diff --git a/_examples/i18n/locales/zh-CN/locale_zh-CN.ini b/_examples/i18n/locales/zh-CN/locale_zh-CN.ini deleted file mode 100644 index 0a7c91b05d..0000000000 --- a/_examples/i18n/locales/zh-CN/locale_zh-CN.ini +++ /dev/null @@ -1 +0,0 @@ -hi = 您好,%s \ No newline at end of file diff --git a/_examples/i18n/main.go b/_examples/i18n/main.go deleted file mode 100644 index e0a859b47d..0000000000 --- a/_examples/i18n/main.go +++ /dev/null @@ -1,95 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func newApp() *iris.Application { - app := iris.New() - - // Configure i18n. - // First parameter: Glob filpath patern, - // Second variadic parameter: Optional language tags, the first one is the default/fallback one. - err := app.I18n.Load("./locales/*/*.ini", "en-US", "el-GR", "zh-CN") - if err != nil { - panic(err) - } - // app.I18n.LoadAssets for go-bindata. - - // Default values: - // app.I18n.URLParameter = "lang" - // app.I18n.Subdomain = true - // - // Set to false to disallow path (local) redirects, - // see https://github.com/kataras/iris/issues/1369. - // app.I18n.PathRedirect = true - // - // See `app.I18n.ExtractFunc = func(ctx iris.Context) string` or - // `ctx.SetLanguage(langCode string)` to change the extracted language from a request. - - app.Get("/", func(ctx iris.Context) { - hi := ctx.Tr("hi", "iris") - - locale := ctx.GetLocale() - - ctx.Writef("From the language %s translated output: %s", locale.Language(), hi) - }) - - app.Get("/some-path", func(ctx iris.Context) { - ctx.Writef("%s", ctx.Tr("hi", "iris")) - }) - - app.Get("/other", func(ctx iris.Context) { - language := ctx.GetLocale().Language() - - fromFirstFileValue := ctx.Tr("key1") - fromSecondFileValue := ctx.Tr("key2") - ctx.Writef("From the language: %s, translated output:\n%s=%s\n%s=%s", - language, "key1", fromFirstFileValue, - "key2", fromSecondFileValue) - }) - - // using in inside your views: - view := iris.HTML("./views", ".html") - app.RegisterView(view) - - app.Get("/templates", func(ctx iris.Context) { - ctx.View("index.html", iris.Map{ - "tr": ctx.Tr, // word, arguments... {call .tr "hi" "iris"}} - }) - - // Note that, - // Iris automatically adds a "tr" global template function as well, - // the only difference is the way you call it inside your templates and - // that it accepts a language code as its first argument: {{ tr "el-GR" "hi" "iris"}} - }) - // - - return app -} - -func main() { - app := newApp() - - // go to http://localhost:8080/el-gr/some-path - // ^ (by path prefix) - // - // or http://el.mydomain.com8080/some-path - // ^ (by subdomain - test locally with the hosts file) - // - // or http://localhost:8080/zh-CN/templates - // ^ (by path prefix with uppercase) - // - // or http://localhost:8080/some-path?lang=el-GR - // ^ (by url parameter) - // - // or http://localhost:8080 (default is en-US) - // or http://localhost:8080/?lang=zh-CN - // - // go to http://localhost:8080/other?lang=el-GR - // or http://localhost:8080/other (default is en-US) - // or http://localhost:8080/other?lang=en-US - // - // or use cookies to set the language. - app.Listen(":8080", iris.WithSitemap("http://localhost:8080")) -} diff --git a/_examples/i18n/main_test.go b/_examples/i18n/main_test.go deleted file mode 100644 index f625c29c5f..0000000000 --- a/_examples/i18n/main_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package main - -import ( - "fmt" - "strings" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestI18n(t *testing.T) { - app := newApp() - - const ( - expectedf = "From the language %s translated output: %s" - - enUS = "hello, iris" - elGR = "γεια, iris" - zhCN = "您好,iris" - ) - - var ( - tests = map[string]string{ - "en-US": fmt.Sprintf(expectedf, "en-US", enUS), - "el-GR": fmt.Sprintf(expectedf, "el-GR", elGR), - "zh-CN": fmt.Sprintf(expectedf, "zh-CN", zhCN), - } - - elgrMulti = fmt.Sprintf("From the language: %s, translated output:\n%s=%s\n%s=%s", "el-GR", - "key1", - "αυτό είναι μια τιμή από το πρώτο αρχείο: locale_multi_first", - "key2", - "αυτό είναι μια τιμή από το δεύτερο αρχείο μετάφρασης: locale_multi_second") - enusMulti = fmt.Sprintf("From the language: %s, translated output:\n%s=%s\n%s=%s", "en-US", - "key1", - "this is a value from the first file: locale_multi_first", - "key2", - "this is a value from the second file: locale_multi_second") - ) - - e := httptest.New(t, app) - // default should be en-US. - e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal(tests["en-US"]) - - for lang, body := range tests { - e.GET("/").WithQueryString("lang=" + lang).Expect().Status(httptest.StatusOK). - Body().Equal(body) - - // test lowercase. - e.GET("/").WithQueryString("lang=" + strings.ToLower(lang)).Expect().Status(httptest.StatusOK). - Body().Equal(body) - - // test first part (e.g. en instead of en-US). - langFirstPart := strings.Split(lang, "-")[0] - e.GET("/").WithQueryString("lang=" + langFirstPart).Expect().Status(httptest.StatusOK). - Body().Equal(body) - - // test accept-language header prefix (i18n wrapper). - e.GET("/"+lang).WithHeader("Accept-Language", lang).Expect().Status(httptest.StatusOK). - Body().Equal(body) - - // test path prefix (i18n router wrapper). - e.GET("/" + lang).Expect().Status(httptest.StatusOK). - Body().Equal(body) - - // test path prefix with first part. - e.GET("/" + langFirstPart).Expect().Status(httptest.StatusOK). - Body().Equal(body) - } - - e.GET("/other").WithQueryString("lang=el-GR").Expect().Status(httptest.StatusOK). - Body().Equal(elgrMulti) - e.GET("/other").WithQueryString("lang=en-US").Expect().Status(httptest.StatusOK). - Body().Equal(enusMulti) - - // test path prefix (i18n router wrapper). - e.GET("/el-gr/other").Expect().Status(httptest.StatusOK). - Body().Equal(elgrMulti) - e.GET("/en/other").Expect().Status(httptest.StatusOK). - Body().Equal(enusMulti) - - e.GET("/el-GRtemplates").Expect().Status(httptest.StatusNotFound) - e.GET("/el-templates").Expect().Status(httptest.StatusNotFound) - - e.GET("/el/templates").Expect().Status(httptest.StatusOK).Body().Contains(elGR).Contains(zhCN) -} diff --git a/_examples/i18n/views/index.html b/_examples/i18n/views/index.html deleted file mode 100644 index 32bc8a6b24..0000000000 --- a/_examples/i18n/views/index.html +++ /dev/null @@ -1,9 +0,0 @@ -

Test translate current locale template function [dynamic] ("word", arguments...)
call .tr "hi" "iris"

- -{{call .tr "hi" "iris"}} - -
- -

Test translate of any language template function [static] ("language", "word", arguments...)
tr "zh-CN" "hi" "iris"

- -{{tr "zh-CN" "hi" "iris"}} \ No newline at end of file diff --git a/_examples/kafka-api/0_docs.png b/_examples/kafka-api/0_docs.png deleted file mode 100644 index 2c81a744de..0000000000 Binary files a/_examples/kafka-api/0_docs.png and /dev/null differ diff --git a/_examples/kafka-api/1_create_topic.png b/_examples/kafka-api/1_create_topic.png deleted file mode 100644 index 15f172d6bb..0000000000 Binary files a/_examples/kafka-api/1_create_topic.png and /dev/null differ diff --git a/_examples/kafka-api/2_list_topics.png b/_examples/kafka-api/2_list_topics.png deleted file mode 100644 index 8ff6df54d6..0000000000 Binary files a/_examples/kafka-api/2_list_topics.png and /dev/null differ diff --git a/_examples/kafka-api/3_store_to_topic.png b/_examples/kafka-api/3_store_to_topic.png deleted file mode 100644 index c0fc81142e..0000000000 Binary files a/_examples/kafka-api/3_store_to_topic.png and /dev/null differ diff --git a/_examples/kafka-api/4_retrieve_from_topic_real_time.png b/_examples/kafka-api/4_retrieve_from_topic_real_time.png deleted file mode 100644 index b336205baf..0000000000 Binary files a/_examples/kafka-api/4_retrieve_from_topic_real_time.png and /dev/null differ diff --git a/_examples/kafka-api/Dockerfile b/_examples/kafka-api/Dockerfile deleted file mode 100644 index dec864fef4..0000000000 --- a/_examples/kafka-api/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# docker build -t myapp . -# docker run --rm -it -p 8080:8080 myapp:latest -FROM golang:latest AS builder -RUN apt-get update -ENV GO111MODULE=on \ - CGO_ENABLED=0 \ - GOOS=linux \ - GOARCH=amd64 -WORKDIR /go/src/app -COPY go.mod . -RUN go mod download -COPY . . -RUN go install - -FROM scratch -COPY --from=builder /go/bin/myapp . -ENTRYPOINT ["./myapp"] \ No newline at end of file diff --git a/_examples/kafka-api/README.md b/_examples/kafka-api/README.md deleted file mode 100644 index c9db260f19..0000000000 --- a/_examples/kafka-api/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Writing an API for Apache Kafka with Iris - -Read the [code](main.go). - -## Docker - -1. Open [docker-compose.yml](docker-compose.yml) and replace `KAFKA_ADVERTISED_HOST_NAME` with your own local address -2. Install [Docker](https://www.docker.com/) -3. Execute the command below to start kafka stack and the go application: - -```sh -$ docker-compose up -``` - -## Manually - -Install & run Kafka and Zookeper locally and then: - -```sh -go run main.go -``` - -## Screens - -![](0_docs.png) - -![](1_create_topic.png) - -![](2_list_topics.png) - -![](3_store_to_topic.png) - -![](4_retrieve_from_topic_real_time.png) diff --git a/_examples/kafka-api/docker-compose.yml b/_examples/kafka-api/docker-compose.yml deleted file mode 100644 index 004c3ba057..0000000000 --- a/_examples/kafka-api/docker-compose.yml +++ /dev/null @@ -1,29 +0,0 @@ -version: '3.1' - -services: - zookeeper: - image: wurstmeister/zookeeper - ports: - - 2181:2181 - kafka: - image: wurstmeister/kafka - ports: - - 9092:9092 - environment: - KAFKA_ADVERTISED_HOST_NAME: 10.122.1.109 # replace that with your own local ipv4 addr. - KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 - # kafka: - # image: confluentinc/cp-kafka:5.5.0 - # hostname: kafka - # ports: - # - 9092:9092 - # environment: - # KAFKA_ADVERTISED_LISTENERS: LISTENER_DOCKER_INTERNAL://kafka:19092,LISTENER_DOCKER_EXTERNAL://${DOCKER_HOST_IP:-127.0.0.1}:9092 - app: - build: . - ports: - - 8080:8080 - environment: - KAFKA_1: kafka:9092 - depends_on: - - kafka \ No newline at end of file diff --git a/_examples/kafka-api/go.mod b/_examples/kafka-api/go.mod deleted file mode 100644 index 101fed8bb3..0000000000 --- a/_examples/kafka-api/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module myapp - -go 1.15 - -require ( - github.com/Shopify/sarama v1.26.4 - github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd -) diff --git a/_examples/kafka-api/main.go b/_examples/kafka-api/main.go deleted file mode 100644 index 4d00e17a4e..0000000000 --- a/_examples/kafka-api/main.go +++ /dev/null @@ -1,394 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "strings" - "time" - - "github.com/Shopify/sarama" - "github.com/kataras/iris/v12" -) - -/* -First of all, read about Apache Kafka, install and run it, if you didn't already: https://kafka.apache.org/quickstart - -Secondly, install your favourite Go library for Apache Kafka communication. -I have chosen the shopify's one although I really loved the `segmentio/kafka-go` as well but it needs more to be done there -and you will be bored to read all the necessary code required to get started with it, so: - $ go get -u github.com/Shopify/sarama - -The minimum Apache Kafka broker(s) version required is 0.10.0.0 but 0.11.x+ is recommended (tested with 2.5.0). - -Resources: - - https://github.com/apache/kafka - - https://github.com/Shopify/sarama/blob/master/examples/http_server/http_server.go - - DIY -*/ - -// package-level variables for the sake of the example -// but you can define them inside your main func -// and pass around this config whenever you need to create a client or a producer or a consumer or use a cluster. -var ( - // The Kafka brokers to connect to, as a comma separated list. - brokers = []string{getenv("KAFKA_1", "localhost:9092")} - // The config which makes our live easier when passing around, it pre-mades a lot of things for us. - config *sarama.Config -) - -func getenv(key string, def string) string { - if value := os.Getenv(key); value != "" { - return value - } - - return def -} - -func init() { - config = sarama.NewConfig() - config.ClientID = "iris-example-client" - config.Version = sarama.V0_11_0_2 - // config.Producer.RequiredAcks = sarama.WaitForAll // Wait for all in-sync replicas to ack the message. - config.Producer.Compression = sarama.CompressionSnappy - config.Producer.Flush.Frequency = 500 * time.Millisecond - config.Producer.Retry.Max = 10 // Retry up to 10 times to produce the message. - config.Producer.Return.Successes = true - - // for SASL/basic plain text authentication: config.Net.SASL. - // config.Net.SASL.Enable = true - // config.Net.SASL.Handshake = false - // config.Net.SASL.User = "myuser" - // config.Net.SASL.Password = "mypass" - - config.Consumer.Return.Errors = true -} - -func main() { - app := iris.New() - app.OnErrorCode(iris.StatusNotFound, handleNotFound) - - v1 := app.Party("/api/v1") - { - topicsAPI := v1.Party("/topics") - { - topicsAPI.Post("/", postTopicsHandler) // create a topic. - topicsAPI.Get("/", getTopicsHandler) // list all topics. - - topicsAPI.Post("/{topic}/produce", postTopicProduceHandler) // store to a topic. - topicsAPI.Get("/{topic}/consume", getTopicConsumeSSEHandler) // retrieve all messages from a topic. - } - } - - app.Get("/", docsHandler) - - app.Logger().Infof("Brokers: %s", strings.Join(brokers, ", ")) - // GET : http://localhost:8080 - // POST, GET: http://localhost:8080/api/v1/topics - // POST : http://localhost:8080/apiv1/topics/{topic}/produce?key=my-key - // GET : http://localhost:8080/apiv1/topics/{topic}/consume?partition=0&offset=0 - app.Listen(":8080") -} - -// simple use-case, you can use templates and views obviously, see the "_examples/views" examples. -func docsHandler(ctx iris.Context) { - ctx.ContentType("text/html") // or ctx.HTML(fmt.Sprintf(...)) - ctx.Writef(` - - - - `) - defer ctx.Writef("") - - ctx.Writef("") - defer ctx.Writef("") - - ctx.Writef(` - - - - - - - `) - defer ctx.Writef(`
MethodPathHandler
`) - - registeredRoutes := ctx.Application().GetRoutesReadOnly() - for _, r := range registeredRoutes { - if r.Path() == "/" { // don't list the root, current one. - continue - } - - ctx.Writef(` - - %s - %s%s - %s - - `, r.Method(), ctx.Host(), r.Path(), r.MainHandlerName()) - } -} - -type httpError struct { - Code int `json:"code"` - Reason string `json:"reason"` -} - -func (h httpError) Error() string { - return fmt.Sprintf("Status Code: %d\nReason: %s", h.Code, h.Reason) -} - -func fail(ctx iris.Context, statusCode int, format string, a ...interface{}) { - reason := "unspecified" - if format != "" { - reason = fmt.Sprintf(format, a...) - } - - err := httpError{ - Code: statusCode, - Reason: reason, - } - - ctx.StopWithJSON(statusCode, err) -} - -func handleNotFound(ctx iris.Context) { - suggestPaths := ctx.FindClosest(3) - if len(suggestPaths) == 0 { - ctx.WriteString("not found") - return - } - - ctx.HTML("Did you mean?
    ") - for _, s := range suggestPaths { - ctx.HTML(`
  • %s
  • `, s, s) - } - ctx.HTML("
") -} - -// Topic the payload for a kafka topic creation. -type Topic struct { - Topic string `json:"topic"` - Partitions int32 `json:"partitions"` - ReplicationFactor int16 `json:"replication"` - Configs []kv `json:"configs,omitempty"` -} - -type kv struct { - Key string `json:"key"` - Value string `json:"value"` -} - -func createKafkaTopic(t Topic) error { - cluster, err := sarama.NewClusterAdmin(brokers, config) - if err != nil { - return err - } - defer cluster.Close() - - topicName := t.Topic - topicDetail := sarama.TopicDetail{ - NumPartitions: t.Partitions, - ReplicationFactor: t.ReplicationFactor, - } - - if len(t.Configs) > 0 { - topicDetail.ConfigEntries = make(map[string]*string, len(t.Configs)) - for _, c := range t.Configs { - topicDetail.ConfigEntries[c.Key] = &c.Value // generate a ptr, or fill a new(string) with it and use that. - } - } - - return cluster.CreateTopic(topicName, &topicDetail, false) -} - -func postTopicsHandler(ctx iris.Context) { - var t Topic - err := ctx.ReadJSON(&t) - if err != nil { - fail(ctx, iris.StatusBadRequest, - "received invalid topic payload: %v", err) - return - } - - // try to create the topic inside kafka. - err = createKafkaTopic(t) - if err != nil { - fail(ctx, iris.StatusInternalServerError, - "unable to create topic: %v", err) - return - } - - // unnecessary statement but it's here to show you that topic is created, - // depending on your API expectations and how you used to work - // you may want to change the status code to something like `iris.StatusCreated`. - ctx.StatusCode(iris.StatusOK) -} - -func getKafkaTopics() ([]string, error) { - client, err := sarama.NewClient(brokers, config) - if err != nil { - return nil, err - } - defer client.Close() - - return client.Topics() -} - -func getTopicsHandler(ctx iris.Context) { - topics, err := getKafkaTopics() - if err != nil { - fail(ctx, iris.StatusInternalServerError, - "unable to retrieve topics: %v", err) - return - } - - ctx.JSON(topics) -} - -func produceKafkaMessage(toTopic string, key string, value []byte) (partition int32, offset int64, err error) { - // On the broker side, you may want to change the following settings to get - // stronger consistency guarantees: - // - For your broker, set `unclean.leader.election.enable` to false - // - For the topic, you could increase `min.insync.replicas`. - - producer, err := sarama.NewSyncProducer(brokers, config) - if err != nil { - return -1, -1, err - } - defer producer.Close() - - // We are not setting a message key, which means that all messages will - // be distributed randomly over the different partitions. - return producer.SendMessage(&sarama.ProducerMessage{ - Topic: toTopic, - Key: sarama.StringEncoder(key), - Value: sarama.ByteEncoder(value), - }) -} - -func postTopicProduceHandler(ctx iris.Context) { - topicName := ctx.Params().Get("topic") - key := ctx.URLParamDefault("key", "default") - - // read the request data and store them as they are (not recommended in production ofcourse, do your own checks here). - body, err := ioutil.ReadAll(ctx.Request().Body) - if err != nil { - fail(ctx, iris.StatusUnprocessableEntity, "unable to read your data: %v", err) - return - } - - partition, offset, err := produceKafkaMessage(topicName, key, body) - if err != nil { - fail(ctx, iris.StatusInternalServerError, "failed to store your data: %v", err) - return - } - - // The tuple (topic, partition, offset) can be used as a unique identifier - // for a message in a Kafka cluster. - ctx.Writef("Your data is stored with unique identifier: %s/%d/%d", topicName, partition, offset) -} - -type message struct { - Time time.Time `json:"time"` - Key string `json:"key"` - // Value []byte/json.RawMessage(if you are sure that you are sending only JSON) `json:"value"` - // or: - Value string `json:"value"` // for simple key-value storage. -} - -func getTopicConsumeSSEHandler(ctx iris.Context) { - flusher, ok := ctx.ResponseWriter().Flusher() - if !ok { - ctx.StopWithText(iris.StatusHTTPVersionNotSupported, "streaming unsupported") - return - } - - ctx.ContentType("application/json, text/event-stream") - ctx.Header("Cache-Control", "no-cache") - ctx.Header("Connection", "keep-alive") - - master, err := sarama.NewConsumer(brokers, config) - if err != nil { - fail(ctx, iris.StatusInternalServerError, "unable to start master consumer: %v", err) - return - } - - fromTopic := ctx.Params().Get("topic") - // take the partition, defaults to the first found if not url query parameter "partition" passed. - var partition int32 - partitions, err := master.Partitions(fromTopic) - if err != nil { - master.Close() - fail(ctx, iris.StatusInternalServerError, "unable to get partitions for topic: '%s': %v", fromTopic, err) - return - } - - if len(partitions) > 0 { - partition = partitions[0] - } - - partition = ctx.URLParamInt32Default("partition", partition) - offset := ctx.URLParamInt64Default("offset", sarama.OffsetOldest) - - consumer, err := master.ConsumePartition(fromTopic, partition, offset) - if err != nil { - ctx.Application().Logger().Error(err) - master.Close() // close the master here to avoid any leaks, we will exit. - fail(ctx, iris.StatusInternalServerError, "unable to start partition consumer: %v", err) - return - } - - // `OnClose` fires when the request is finally done (all data read and handler exits) or interrupted by the user. - ctx.OnClose(func() { - ctx.Application().Logger().Warnf("a client left") - - // Close shuts down the consumer. It must be called after all child - // PartitionConsumers have already been closed. <-- That is what - // godocs says but it doesn't work like this. - // if err = consumer.Close(); err != nil { - // ctx.Application().Logger().Errorf("[%s] unable to close partition consumer: %v", ctx.RemoteAddr(), err) - // } - // so close the master only and omit the first ^ consumer.Close: - if err = master.Close(); err != nil { - ctx.Application().Logger().Errorf("[%s] unable to close master consumer: %v", ctx.RemoteAddr(), err) - } - }) - - for { - select { - case consumerErr, ok := <-consumer.Errors(): - if !ok { - return - } - ctx.Writef("data: error: {\"reason\": \"%s\"}\n\n", consumerErr.Error()) - flusher.Flush() - case incoming, ok := <-consumer.Messages(): - if !ok { - return - } - - msg := message{ - Time: incoming.Timestamp, - Key: string(incoming.Key), - Value: string(incoming.Value), - } - - b, err := json.Marshal(msg) - if err != nil { - ctx.Application().Logger().Error(err) - continue - } - - ctx.Writef("data: %s\n\n", b) - flusher.Flush() - } - } -} diff --git a/_examples/logging/file-logger/main.go b/_examples/logging/file-logger/main.go deleted file mode 100644 index a9cca07d13..0000000000 --- a/_examples/logging/file-logger/main.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "os" - "time" - - "github.com/kataras/iris/v12" -) - -func main() { - f := newLogFile() - defer f.Close() - - app := iris.New() - // Attach the file as logger, remember, iris' app logger is just an io.Writer. - // Use the following code if you need to write the logs to file and console at the same time. - // app.Logger().SetOutput(io.MultiWriter(f, os.Stdout)) - app.Logger().SetOutput(f) - - app.Get("/ping", func(ctx iris.Context) { - // for the sake of simplicity, in order see the logs at the ./_today_.txt - ctx.Application().Logger().Infof("Request path: %s", ctx.Path()) - ctx.WriteString("pong") - }) - - // Navigate to http://localhost:8080/ping - // and open the ./logs{TODAY}.txt file. - if err := app.Listen(":8080", iris.WithoutBanner); err != nil { - app.Logger().Warn("Shutdown with error: " + err.Error()) - } -} - -// Get a filename based on the date, just for the sugar. -func todayFilename() string { - today := time.Now().Format("Jan 02 2006") - return today + ".txt" -} - -func newLogFile() *os.File { - filename := todayFilename() - // Open the file, this will append to the today's file if server restarted. - f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) - if err != nil { - panic(err) - } - - return f -} diff --git a/_examples/logging/json-logger/main.go b/_examples/logging/json-logger/main.go deleted file mode 100644 index 7147066407..0000000000 --- a/_examples/logging/json-logger/main.go +++ /dev/null @@ -1,87 +0,0 @@ -package main - -import ( - "encoding/json" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/requestid" - - "github.com/kataras/golog" -) - -func main() { - app := iris.New() - app.Logger().SetLevel("debug") - app.Logger().Handle(jsonOutput) - - app.Use(requestid.New()) - - /* Example Output: - { - "timestamp": 1591422944, - "level": "debug", - "message": "This is a message with data", - "fields": { - "username": "kataras" - }, - "stacktrace": [ - { - "function": "main.main", - "source": "C:/mygopath/src/github.com/kataras/iris/_examples/logging/json-logger/main.go:16" - } - ] - } - */ - app.Logger().Debugf("This is a %s with data (debug prints the stacktrace too)", "message", golog.Fields{ - "username": "kataras", - }) - - /* Example Output: - { - "timestamp": 1591422944, - "level": "info", - "message": "An info message", - "fields": { - "home": "https://iris-go.com" - } - } - */ - app.Logger().Infof("An info message", golog.Fields{"home": "https://iris-go.com"}) - - app.Get("/ping", ping) - - // Navigate to http://localhost:8080/ping. - app.Listen(":8080" /*, iris.WithoutBanner*/) -} - -func jsonOutput(l *golog.Log) bool { - enc := json.NewEncoder(l.Logger.Printer) // you can change the output to a file as well. - enc.SetIndent("", " ") - err := enc.Encode(l) - return err == nil -} - -func ping(ctx iris.Context) { - /* Example Output: - { - "timestamp": 1591423046, - "level": "debug", - "message": "Request path: /ping", - "fields": { - "request_id": "fc12d88a-a338-4bb9-aa5e-126f2104365c" - }, - "stacktrace": [ - { - "function": "main.ping", - "source": "C:/mygopath/src/github.com/kataras/iris/_examples/logging/json-logger/main.go:82" - }, - ... - ] - } - */ - ctx.Application().Logger().Debugf("Request path: %s", ctx.Path(), golog.Fields{ - "request_id": ctx.GetID(), - }) - - ctx.WriteString("pong") -} diff --git a/_examples/logging/json-logger/main_test.go b/_examples/logging/json-logger/main_test.go deleted file mode 100644 index 70f02d853f..0000000000 --- a/_examples/logging/json-logger/main_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "strings" - "sync" - "testing" - - "github.com/kataras/golog" - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/httptest" -) - -func TestJSONLogger(t *testing.T) { - iters := 500 - - out := new(bytes.Buffer) - - app := iris.New() - app.Logger().SetTimeFormat("") // disable timestamps. - app.Logger().SetStacktraceLimit(1) // limit debug stacktrace to 1, show only the first caller. - app.Logger().SetOutput(out) - - app.Logger().Handle(func(l *golog.Log) bool { - enc := json.NewEncoder(l.Logger.Printer) // you can change the output to a file as well. - err := enc.Encode(l) - return err == nil - }) - - app.Get("/ping", ping) - - const expectedLogStr = `{"level":"debug","message":"Request path: /ping","fields":{"request_id":null},"stacktrace":[{"function":"json-logger/ping","source":"C:/mygopath/src/github.com/kataras/iris/_examples/logging/json-logger/main.go:82"}]}` - e := httptest.New(t, app, httptest.LogLevel("debug")) - wg := new(sync.WaitGroup) - wg.Add(iters) - for i := 0; i < iters; i++ { - go func() { - e.GET("/ping").Expect().Status(httptest.StatusOK).Body().Equal("pong") - wg.Done() - }() - } - - wg.Wait() - expected := "" - for i := 0; i < iters; i++ { - expected += expectedLogStr + "\n" - } - - got := out.String() - got = got[strings.Index(got, "{"):] // take only the json we care and after. - if expected != got { - if !strings.HasSuffix(got, expected) { - // C:/mygopath vs /home/travis vs any file system, - // pure check but it does the job. - t.Fatalf("expected:\n%s\nbut got:\n%s", expected, got) - } - } -} diff --git a/_examples/logging/request-logger/main.go b/_examples/logging/request-logger/main.go deleted file mode 100644 index 1e89d833d1..0000000000 --- a/_examples/logging/request-logger/main.go +++ /dev/null @@ -1,65 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/logger" -) - -func main() { - app := iris.New() - - customLogger := logger.New(logger.Config{ - // Status displays status code - Status: true, - // IP displays request's remote address - IP: true, - // Method displays the http method - Method: true, - // Path displays the request path - Path: true, - // Query appends the url query to the Path. - Query: true, - - // Columns: true, - - // if !empty then its contents derives from `ctx.Values().Get("logger_message") - // will be added to the logs. - MessageContextKeys: []string{"logger_message"}, - - // if !empty then its contents derives from `ctx.GetHeader("User-Agent") - MessageHeaderKeys: []string{"User-Agent"}, - }) - - app.Use(customLogger) - - h := func(ctx iris.Context) { - ctx.Writef("Hello from %s", ctx.Path()) - } - app.Get("/", h) - - app.Get("/1", h) - - app.Get("/2", h) - - // http errors have their own handlers, therefore - // registering a middleare should be done manually. - /* - app.OnErrorCode(404 ,customLogger, func(ctx iris.Context) { - ctx.Writef("My Custom 404 error page ") - }) - */ - // or catch all http errors: - app.OnAnyErrorCode(customLogger, func(ctx iris.Context) { - // this should be added to the logs, at the end because of the `logger.Config#MessageContextKey` - ctx.Values().Set("logger_message", - "a dynamic message passed to the logs") - ctx.Writef("My Custom error page") - }) - - // http://localhost:8080 - // http://localhost:8080/1 - // http://localhost:8080/2 - // http://lcoalhost:8080/notfoundhere - // see the output on the console. - app.Listen(":8080") -} diff --git a/_examples/logging/request-logger/request-logger-file-json/main.go b/_examples/logging/request-logger/request-logger-file-json/main.go deleted file mode 100644 index 67480502ca..0000000000 --- a/_examples/logging/request-logger/request-logger-file-json/main.go +++ /dev/null @@ -1,110 +0,0 @@ -package main - -import ( - "fmt" - "io" - "os" - "strings" - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/logger" -) - -const deleteFileOnExit = false - -func newRequestLogger(newWriter io.Writer) iris.Handler { - c := logger.Config{} - c.AddSkipper(func(ctx iris.Context) bool { - path := ctx.Path() - for _, ext := range excludeExtensions { - if strings.HasSuffix(path, ext) { - return true - } - } - return false - }) - - c.LogFuncCtx = func(ctx iris.Context, latency time.Duration) { - datetime := time.Now().Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat()) - customHandlerMessage := ctx.Values().GetString("log_message") - - file, line := ctx.HandlerFileLine() - source := fmt.Sprintf("%s:%d", file, line) - - // this will just append a line without an array of javascript objects, readers of this file should read one line per log javascript object, - // however, you can improve it even more, this is just a simple example on how to use the `LogFuncCtx`. - jsonStr := fmt.Sprintf(`{"datetime":"%s","level":"%s","source":"%s","latency": "%s","status": %d,"method":"%s","path":"%s","message":"%s"}`, - datetime, "INFO", source, latency.String(), ctx.GetStatusCode(), ctx.Method(), ctx.Path(), customHandlerMessage) - - fmt.Fprintln(newWriter, jsonStr) - } - - return logger.New(c) -} - -func h(ctx iris.Context) { - ctx.Values().Set("log_message", "something to give more info to the request logger") - - ctx.Writef("Hello from %s", ctx.Path()) -} - -func main() { - app := iris.New() - - logFile := newLogFile() - defer func() { - logFile.Close() - if deleteFileOnExit { - os.Remove(logFile.Name()) - } - }() - - r := newRequestLogger(logFile) - app.Use(r) - - app.OnAnyErrorCode(r, func(ctx iris.Context) { - ctx.HTML("

Error: Please try this instead.

") - }) - - app.Get("/", h) - - app.Get("/1", h) - - app.Get("/2", h) - - app.Get("/", h) - - // http://localhost:8080 - // http://localhost:8080/1 - // http://localhost:8080/2 - // http://lcoalhost:8080/notfoundhere - app.Listen(":8080") -} - -var excludeExtensions = [...]string{ - ".js", - ".css", - ".jpg", - ".png", - ".ico", - ".svg", -} - -// get a filename based on the date, file logs works that way the most times -// but these are just a sugar. -func todayFilename() string { - today := time.Now().Format("Jan 02 2006") - return today + ".json" -} - -func newLogFile() *os.File { - filename := todayFilename() - // open an output file, this will append to the today's file if server restarted. - f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) - if err != nil { - panic(err) - } - - return f -} diff --git a/_examples/logging/request-logger/request-logger-file/main.go b/_examples/logging/request-logger/request-logger-file/main.go deleted file mode 100644 index ac387963f9..0000000000 --- a/_examples/logging/request-logger/request-logger-file/main.go +++ /dev/null @@ -1,107 +0,0 @@ -package main - -import ( - "os" - "strings" - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/logger" -) - -const deleteFileOnExit = false - -func main() { - app := iris.New() - r, close := newRequestLogger() - defer close() - - app.Use(r) - app.OnAnyErrorCode(r, func(ctx iris.Context) { - ctx.HTML("

Error: Please try this instead.

") - }) - - h := func(ctx iris.Context) { - ctx.Writef("Hello from %s", ctx.Path()) - } - - app.Get("/", h) - - app.Get("/1", h) - - app.Get("/2", h) - - // http://localhost:8080 - // http://localhost:8080/1 - // http://localhost:8080/2 - // http://lcoalhost:8080/notfoundhere - app.Listen(":8080") -} - -// get a filename based on the date, file logs works that way the most times -// but these are just a sugar. -func todayFilename() string { - today := time.Now().Format("Jan 02 2006") - return today + ".txt" -} - -func newLogFile() *os.File { - filename := todayFilename() - // open an output file, this will append to the today's file if server restarted. - f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) - if err != nil { - panic(err) - } - - return f -} - -var excludeExtensions = [...]string{ - ".js", - ".css", - ".jpg", - ".png", - ".ico", - ".svg", -} - -func newRequestLogger() (h iris.Handler, close func() error) { - close = func() error { return nil } - - c := logger.Config{ - Status: true, - IP: true, - Method: true, - Path: true, - Columns: true, - } - - logFile := newLogFile() - close = func() error { - err := logFile.Close() - if deleteFileOnExit { - err = os.Remove(logFile.Name()) - } - return err - } - - c.LogFunc = func(endTime time.Time, latency time.Duration, status, ip, method, path string, message interface{}, headerMessage interface{}) { - output := logger.Columnize(endTime.Format("2006/01/02 - 15:04:05"), latency, status, ip, method, path, message, headerMessage) - logFile.Write([]byte(output)) - } // or make use of the `LogFuncCtx`, see the '../request-logger-file-json' example for more. - - // when we don't want to use to log requests to assets and etc. - c.AddSkipper(func(ctx iris.Context) bool { - path := ctx.Path() - for _, ext := range excludeExtensions { - if strings.HasSuffix(path, ext) { - return true - } - } - return false - }) - - h = logger.New(c) - - return -} diff --git a/_examples/logging/rollbar/go.mod b/_examples/logging/rollbar/go.mod deleted file mode 100644 index 6a3e5ca1e7..0000000000 --- a/_examples/logging/rollbar/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/kataras/iris/examples/logging/rollbar - -go 1.15 - -require ( - github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd - github.com/rollbar/rollbar-go v1.2.0 -) diff --git a/_examples/logging/rollbar/main.go b/_examples/logging/rollbar/main.go deleted file mode 100644 index 0903f886f8..0000000000 --- a/_examples/logging/rollbar/main.go +++ /dev/null @@ -1,107 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "os" - "runtime/debug" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/requestid" - - "github.com/rollbar/rollbar-go" -) - -// * https://rollbar.com/signup -// * https://docs.rollbar.com/docs/go -func init() { - token := os.Getenv("ROLLBAR_TOKEN") // replace that with your token. - if token == "" { - panic("ROLLBAR_TOKEN is missing") - } - - // rb := rollbar.NewAsync(token, "production", "", hostname, "github.com/kataras/iris") - // Or use the package-level instance: - rollbar.SetToken(token) - // defaults to "development" - rollbar.SetEnvironment("production") - // optional Git hash/branch/tag (required for GitHub integration) - // rollbar.SetCodeVersion("v2") - // optional override; defaults to hostname - // rollbar.SetServerHost("web.1") - // path of project (required for GitHub integration and non-project stacktrace collapsing) - rollbar.SetServerRoot("github.com/kataras/iris") - -} - -func main() { - app := iris.New() - // A middleware which sets the ctx.GetID (or requestid.Get(ctx)). - app.Use(requestid.New()) - // A recover middleware which sends the error trace to the rollbar. - app.Use(func(ctx iris.Context) { - defer func() { - if r := recover(); r != nil { - debug.PrintStack() - - file, line := ctx.HandlerFileLine() // the failed handler's source code position. - - // cause other info - rollbar.Critical(errors.New(fmt.Sprint(r)), iris.Map{ - "request_id": ctx.GetID(), - "request_ip": ctx.RemoteAddr(), - "request_uri": ctx.FullRequestURI(), - "handler": iris.Map{ - "name": ctx.HandlerName(), // the handler which failed. - "file": fmt.Sprintf("%s:%d", file, line), - }, - }) - - ctx.StopWithStatus(iris.StatusInternalServerError) - } - }() - - ctx.Next() - }) - - app.Get("/", index) - app.Get("/panic", panicMe) - - // http://localhost:8080 should add an info message to the rollbar's "Items" dashboard. - // http://localhost:8080/panic should add a critical message to the rollbar's "Items" dashboard, - // with the corresponding information appending on its "Occurrences" tab item, e.g: - // Timestamp (PDT) - // * 2020-06-08 04:47 pm - // - // server.host - // * DESKTOP-HOSTNAME - // - // trace_chain.0.exception.message - // * a critical error message here - // - // custom.handler.file - // * C:/mygopath/src/github.com/kataras/iris/_examples/logging/rollbar/main.go:76 - // - // custom.handler.name - // * main.panicMe - // - // custom.request_id - // * cce61665-0c1b-4fb5-8547-06a3537e477c - // - // custom.request_ip - // * ::1 - // - // custom.request_uri - // * http://localhost:8080/panic - app.Listen(":8080") -} - -func index(ctx iris.Context) { - rollbar.Info(fmt.Sprintf("Index page requested by %s", ctx.RemoteAddr())) - - ctx.HTML("

Index Page

") -} - -func panicMe(ctx iris.Context) { - panic("a critical error message here") -} diff --git a/_examples/mvc/authenticated-controller/main.go b/_examples/mvc/authenticated-controller/main.go deleted file mode 100644 index 25a27955d5..0000000000 --- a/_examples/mvc/authenticated-controller/main.go +++ /dev/null @@ -1,110 +0,0 @@ -// Package main shows how to use a dependency to check if a user is logged in -// using a special custom Go type `Authenticated`, which when, -// present on a controller's method or a field then -// it limits the visibility to "authenticated" users only. -// -// The same result could be done through a middleware as well, however using a static type -// any person reads your code can see that an "X" controller or method -// should only be used by "authenticated" users. -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" - "github.com/kataras/iris/v12/sessions" -) - -func main() { - app := newApp() - app.Logger().SetLevel("debug") - - // Open a client, e.g. Postman and visit the below endpoints. - // GET: http://localhost:8080/user (UnauthenticatedUserController.Get) - // POST: http://localhost:8080/user/login (UnauthenticatedUserController.PostLogin) - // GET: http://localhost:8080/user (UserController.Get) - // POST: http://localhost:8080/user/logout (UserController.PostLogout) - app.Listen(":8080") -} - -func newApp() *iris.Application { - app := iris.New() - sess := sessions.New(sessions.Config{ - Cookie: "myapp_session_id", - AllowReclaim: true, - }) - app.Use(sess.Handler()) - - userRouter := app.Party("/user") - { - // Use that in order to be able to register a route twice, - // last one will be executed if the previous route's handler(s) stopped and the response can be reset-ed. - // See core/router/route_register_rule_test.go#TestRegisterRuleOverlap. - userRouter.SetRegisterRule(iris.RouteOverlap) - - // Initialize a new MVC application on top of the "userRouter". - userApp := mvc.New(userRouter) - // Register Dependencies. - userApp.Register(authDependency) - - // Register Controllers. - userApp.Handle(new(UserController)) - userApp.Handle(new(UnauthenticatedUserController)) - } - - return app -} - -// Authenticated is a custom type used as "annotation" for resources that requires authentication, -// its value should be the logged user ID. -type Authenticated uint64 - -func authDependency(ctx iris.Context, session *sessions.Session) Authenticated { - userID := session.GetUint64Default("user_id", 0) - if userID == 0 { - // If execution was stopped - // any controller's method will not be executed at all. - ctx.StopWithStatus(iris.StatusUnauthorized) - return 0 - } - - return Authenticated(userID) -} - -// UnauthenticatedUserController serves the "public" Unauthorized User API. -type UnauthenticatedUserController struct{} - -// Get registers a route that will be executed when authentication is not passed -// (see UserController.Get) too. -func (c *UnauthenticatedUserController) Get() string { - return "custom action to redirect on authentication page" -} - -// PostLogin serves -// POST: /user/login -func (c *UnauthenticatedUserController) PostLogin(session *sessions.Session) mvc.Response { - session.Set("user_id", 1) - - // Redirect (you can still use the Context.Redirect if you want so). - return mvc.Response{ - Path: "/user", - Code: iris.StatusFound, - } -} - -// UserController serves the "public" User API. -type UserController struct { - CurrentUserID Authenticated -} - -// Get returns a message for the sake of the example. -// GET: /user -func (c *UserController) Get() string { - return `UserController.Get: The Authenticated type -can be used to secure a controller's method too.` -} - -// PostLogout serves -// POST: /user/logout -func (c *UserController) PostLogout(ctx iris.Context) { - sessions.Get(ctx).Man.Destroy(ctx) -} diff --git a/_examples/mvc/authenticated-controller/main_test.go b/_examples/mvc/authenticated-controller/main_test.go deleted file mode 100644 index df47e18ec1..0000000000 --- a/_examples/mvc/authenticated-controller/main_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestMVCOverlapping(t *testing.T) { - app := newApp() - - e := httptest.New(t, app, httptest.URL("http://example.com")) - // unauthenticated. - e.GET("/user").Expect().Status(httptest.StatusOK).Body().Equal("custom action to redirect on authentication page") - // login. - e.POST("/user/login").Expect().Status(httptest.StatusOK) - // authenticated. - e.GET("/user").Expect().Status(httptest.StatusOK).Body().Equal(`UserController.Get: The Authenticated type -can be used to secure a controller's method too.`) - // logout. - e.POST("/user/logout").Expect().Status(httptest.StatusOK) - // unauthenticated. - e.GET("/user").Expect().Status(httptest.StatusOK).Body().Equal("custom action to redirect on authentication page") -} diff --git a/_examples/mvc/basic/main.go b/_examples/mvc/basic/main.go deleted file mode 100644 index ebdfcde992..0000000000 --- a/_examples/mvc/basic/main.go +++ /dev/null @@ -1,105 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/recover" - "github.com/kataras/iris/v12/sessions" - - "github.com/kataras/iris/v12/mvc" -) - -func main() { - app := iris.New() - app.Use(recover.New()) - app.Logger().SetLevel("debug") - mvc.Configure(app.Party("/basic"), basicMVC) - - app.Listen(":8080") -} - -func basicMVC(app *mvc.Application) { - // You can use normal middlewares at MVC apps of course. - app.Router.Use(func(ctx iris.Context) { - ctx.Application().Logger().Infof("Path: %s", ctx.Path()) - ctx.Next() - }) - - // Register dependencies which will be binding to the controller(s), - // can be either a function which accepts an iris.Context and returns a single value (dynamic binding) - // or a static struct value (service). - app.Register( - sessions.New(sessions.Config{}).Start, - &prefixedLogger{prefix: "DEV"}, - ) - - // GET: http://localhost:8080/basic - // GET: http://localhost:8080/basic/custom - // GET: http://localhost:8080/basic/custom2 - app.Handle(new(basicController)) - - // All dependencies of the parent *mvc.Application - // are cloned to this new child, - // thefore it has access to the same session as well. - // GET: http://localhost:8080/basic/sub - app.Party("/sub"). - Handle(new(basicSubController)) -} - -// If controller's fields (or even its functions) expecting an interface -// but a struct value is binded then it will check -// if that struct value implements -// the interface and if true then it will add this to the -// available bindings, as expected, before the server ran of course, -// remember? Iris always uses the best possible way to reduce load -// on serving web resources. - -type LoggerService interface { - Log(string) -} - -type prefixedLogger struct { - prefix string -} - -func (s *prefixedLogger) Log(msg string) { - fmt.Printf("%s: %s\n", s.prefix, msg) -} - -type basicController struct { - Logger LoggerService - - Session *sessions.Session -} - -func (c *basicController) BeforeActivation(b mvc.BeforeActivation) { - b.HandleMany("GET", "/custom /custom2", "Custom") -} - -func (c *basicController) AfterActivation(a mvc.AfterActivation) { - if a.Singleton() { - panic("basicController should be stateless, a request-scoped, we have a 'Session' which depends on the context.") - } -} - -func (c *basicController) Get() string { - count := c.Session.Increment("count", 1) - - body := fmt.Sprintf("Hello from basicController\nTotal visits from you: %d", count) - c.Logger.Log(body) - return body -} - -func (c *basicController) Custom() string { - return "custom" -} - -type basicSubController struct { - Session *sessions.Session -} - -func (c *basicSubController) Get() string { - count := c.Session.GetIntDefault("count", 1) - return fmt.Sprintf("Hello from basicSubController.\nRead-only visits count: %d", count) -} diff --git a/_examples/mvc/basic/wildcard/main.go b/_examples/mvc/basic/wildcard/main.go deleted file mode 100644 index 915d3e3d19..0000000000 --- a/_examples/mvc/basic/wildcard/main.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" -) - -func main() { - app := iris.New() - usersRouter := app.Party("/users") - mvc.New(usersRouter).Handle(new(myController)) - // Same as: - // usersRouter.Get("/{p:path}", func(ctx iris.Context) { - // wildcardPathParameter := ctx.Params().Get("p") - // ctx.JSON(response{ - // Message: "The path parameter is: " + wildcardPathParameter, - // }) - // }) - - /* - curl --location --request GET 'http://localhost:8080/users/path_segment_1/path_segment_2' - - Expected Output: - { - "message": "The wildcard is: path_segment_1/path_segment_2" - } - */ - app.Listen(":8080") -} - -type myController struct{} - -type response struct { - Message string `json:"message"` -} - -func (c *myController) GetByWildcard(wildcardPathParameter string) response { - return response{ - Message: "The path parameter is: " + wildcardPathParameter, - } -} diff --git a/_examples/mvc/error-handler/main.go b/_examples/mvc/error-handler/main.go deleted file mode 100644 index f22015d94e..0000000000 --- a/_examples/mvc/error-handler/main.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/kataras/iris/v12" - - "github.com/kataras/iris/v12/mvc" -) - -func main() { - app := iris.New() - app.Logger().SetLevel("debug") - - mvcApp := mvc.New(app) - // To all controllers, it can optionally be overridden per-controller - // if the controller contains the `HandleError(ctx iris.Context, err error)` function. - // - mvcApp.HandleError(func(ctx iris.Context, err error) { - ctx.HTML(fmt.Sprintf("%s", err.Error())) - }) - // - mvcApp.Handle(new(myController)) - - // http://localhost:8080 - app.Listen(":8080") -} - -type myController struct { -} - -// overriddes the mvcApp.HandleError function. -func (c *myController) HandleError(ctx iris.Context, err error) { - ctx.HTML(fmt.Sprintf("%s", err.Error())) -} - -func (c *myController) Get() error { - return fmt.Errorf("error here") -} diff --git a/_examples/mvc/grpc-compatible/README.md b/_examples/mvc/grpc-compatible/README.md deleted file mode 100644 index 94a84d4e36..0000000000 --- a/_examples/mvc/grpc-compatible/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# gRPC Iris Example - -## Generate TLS Keys - -```sh -$ openssl genrsa -out server.key 2048 -$ openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 -``` - -## Install the protoc Go plugin - -```sh -$ go get -u github.com/golang/protobuf/protoc-gen-go -``` - -## Generate proto - -```sh -$ protoc -I helloworld/ helloworld/helloworld.proto --go_out=plugins=grpc:helloworld -``` diff --git a/_examples/mvc/grpc-compatible/grpc-client/main.go b/_examples/mvc/grpc-compatible/grpc-client/main.go deleted file mode 100644 index 253b379968..0000000000 --- a/_examples/mvc/grpc-compatible/grpc-client/main.go +++ /dev/null @@ -1,47 +0,0 @@ -// Package main implements a client for Greeter service. -package main - -import ( - "context" - "log" - "os" - "time" - - pb "github.com/kataras/iris/v12/_examples/mvc/grpc-compatible/helloworld" - - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" -) - -const ( - address = "localhost:443" - defaultName = "world" -) - -func main() { - // Set up a connection to the server. - cred, err := credentials.NewClientTLSFromFile("../server.crt", "localhost") - if err != nil { - log.Fatal(err) - } - - conn, err := grpc.Dial(address, grpc.WithTransportCredentials(cred), grpc.WithBlock()) - if err != nil { - log.Fatalf("did not connect: %v", err) - } - defer conn.Close() - c := pb.NewGreeterClient(conn) - - // Contact the server and print out its response. - name := defaultName - if len(os.Args) > 1 { - name = os.Args[1] - } - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name}) - if err != nil { - log.Fatalf("could not greet: %v", err) - } - log.Printf("Greeting: %s", r.GetMessage()) -} diff --git a/_examples/mvc/grpc-compatible/helloworld/README.md b/_examples/mvc/grpc-compatible/helloworld/README.md deleted file mode 100644 index 61b3729c3d..0000000000 --- a/_examples/mvc/grpc-compatible/helloworld/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Helloworld gRPC Example - -https://github.com/grpc/grpc-go/tree/master/examples/helloworld \ No newline at end of file diff --git a/_examples/mvc/grpc-compatible/helloworld/helloworld.pb.go b/_examples/mvc/grpc-compatible/helloworld/helloworld.pb.go deleted file mode 100644 index 6be07b7cb6..0000000000 --- a/_examples/mvc/grpc-compatible/helloworld/helloworld.pb.go +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.21.0 -// protoc v3.11.1 -// source: helloworld.proto - -package helloworld - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// The request message containing the user's name. -type HelloRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` -} - -func (x *HelloRequest) Reset() { - *x = HelloRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_helloworld_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *HelloRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HelloRequest) ProtoMessage() {} - -func (x *HelloRequest) ProtoReflect() protoreflect.Message { - mi := &file_helloworld_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. -func (*HelloRequest) Descriptor() ([]byte, []int) { - return file_helloworld_proto_rawDescGZIP(), []int{0} -} - -func (x *HelloRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -// The response message containing the greetings -type HelloReply struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` -} - -func (x *HelloReply) Reset() { - *x = HelloReply{} - if protoimpl.UnsafeEnabled { - mi := &file_helloworld_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *HelloReply) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HelloReply) ProtoMessage() {} - -func (x *HelloReply) ProtoReflect() protoreflect.Message { - mi := &file_helloworld_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HelloReply.ProtoReflect.Descriptor instead. -func (*HelloReply) Descriptor() ([]byte, []int) { - return file_helloworld_proto_rawDescGZIP(), []int{1} -} - -func (x *HelloReply) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -var File_helloworld_proto protoreflect.FileDescriptor - -var file_helloworld_proto_rawDesc = []byte{ - 0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x12, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x22, 0x22, - 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, - 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x49, 0x0a, 0x07, 0x47, 0x72, - 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, - 0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, - 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, - 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, - 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x30, 0x0a, 0x1b, 0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63, - 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, - 0x6f, 0x72, 0x6c, 0x64, 0x42, 0x0f, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_helloworld_proto_rawDescOnce sync.Once - file_helloworld_proto_rawDescData = file_helloworld_proto_rawDesc -) - -func file_helloworld_proto_rawDescGZIP() []byte { - file_helloworld_proto_rawDescOnce.Do(func() { - file_helloworld_proto_rawDescData = protoimpl.X.CompressGZIP(file_helloworld_proto_rawDescData) - }) - return file_helloworld_proto_rawDescData -} - -var file_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_helloworld_proto_goTypes = []interface{}{ - (*HelloRequest)(nil), // 0: helloworld.HelloRequest - (*HelloReply)(nil), // 1: helloworld.HelloReply -} -var file_helloworld_proto_depIdxs = []int32{ - 0, // 0: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest - 1, // 1: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_helloworld_proto_init() } -func file_helloworld_proto_init() { - if File_helloworld_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_helloworld_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HelloRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_helloworld_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HelloReply); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_helloworld_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_helloworld_proto_goTypes, - DependencyIndexes: file_helloworld_proto_depIdxs, - MessageInfos: file_helloworld_proto_msgTypes, - }.Build() - File_helloworld_proto = out.File - file_helloworld_proto_rawDesc = nil - file_helloworld_proto_goTypes = nil - file_helloworld_proto_depIdxs = nil -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConnInterface - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 - -// GreeterClient is the client API for Greeter service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type GreeterClient interface { - // Sends a greeting - SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) -} - -type greeterClient struct { - cc grpc.ClientConnInterface -} - -func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient { - return &greeterClient{cc} -} - -func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { - out := new(HelloReply) - err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// GreeterServer is the server API for Greeter service. -type GreeterServer interface { - // Sends a greeting - SayHello(context.Context, *HelloRequest) (*HelloReply, error) -} - -// UnimplementedGreeterServer can be embedded to have forward compatible implementations. -type UnimplementedGreeterServer struct { -} - -func (*UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) { - return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") -} - -func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) { - s.RegisterService(&_Greeter_serviceDesc, srv) -} - -func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(HelloRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(GreeterServer).SayHello(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/helloworld.Greeter/SayHello", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _Greeter_serviceDesc = grpc.ServiceDesc{ - ServiceName: "helloworld.Greeter", - HandlerType: (*GreeterServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "SayHello", - Handler: _Greeter_SayHello_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "helloworld.proto", -} diff --git a/_examples/mvc/grpc-compatible/helloworld/helloworld.proto b/_examples/mvc/grpc-compatible/helloworld/helloworld.proto deleted file mode 100644 index abb6278323..0000000000 --- a/_examples/mvc/grpc-compatible/helloworld/helloworld.proto +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package helloworld; - -// The greeting service definition. -service Greeter { - // Sends a greeting - rpc SayHello (HelloRequest) returns (HelloReply) {} -} - -// The request message containing the user's name. -message HelloRequest { - string name = 1; -} - -// The response message containing the greetings -message HelloReply { - string message = 1; -} \ No newline at end of file diff --git a/_examples/mvc/grpc-compatible/http-client/main.go b/_examples/mvc/grpc-compatible/http-client/main.go deleted file mode 100644 index f198d3986c..0000000000 --- a/_examples/mvc/grpc-compatible/http-client/main.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "bytes" - "crypto/tls" - "crypto/x509" - "encoding/json" - "io/ioutil" - "log" - "net/http" - - pb "github.com/kataras/iris/v12/_examples/mvc/grpc-compatible/helloworld" -) - -func main() { - b, err := ioutil.ReadFile("../server.crt") - if err != nil { - log.Fatal(err) - } - cp := x509.NewCertPool() - if !cp.AppendCertsFromPEM(b) { - log.Fatal("credentials: failed to append certificates") - } - - transport := &http.Transport{ - TLSClientConfig: &tls.Config{ - RootCAs: cp, - }, - } - - client := http.Client{Transport: transport} - buf := new(bytes.Buffer) - err = json.NewEncoder(buf).Encode(pb.HelloRequest{Name: "world"}) - if err != nil { - log.Fatal(err) - } - - resp, err := client.Post("https://localhost/helloworld.Greeter/SayHello", "application/json", buf) - if err != nil { - log.Fatal(err) - } - defer resp.Body.Close() - - var reply pb.HelloReply - err = json.NewDecoder(resp.Body).Decode(&reply) - if err != nil { - log.Fatal(err) - } - log.Printf("Greeting: %s", reply.GetMessage()) -} diff --git a/_examples/mvc/grpc-compatible/main.go b/_examples/mvc/grpc-compatible/main.go deleted file mode 100644 index f93bf7a14e..0000000000 --- a/_examples/mvc/grpc-compatible/main.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "context" - - pb "github.com/kataras/iris/v12/_examples/mvc/grpc-compatible/helloworld" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" - - "google.golang.org/grpc" -) - -// See https://github.com/kataras/iris/issues/1449 -// Iris automatically binds the standard "context" context.Context to `iris.Context.Request().Context()` -// and any other structure that is not mapping to a registered dependency -// as a payload depends on the request, e.g XML, YAML, Query, Form, JSON. -// -// Useful to use gRPC services as Iris controllers fast and without wrappers. - -func main() { - app := newApp() - app.Logger().SetLevel("debug") - - // The Iris server should ran under TLS (it's a gRPC requirement). - // POST: https://localhost:443/helloworld.Greeter/SayHello - // with request data: {"name": "John"} - // and expected output: {"message": "Hello John"} - app.Run(iris.TLS(":443", "server.crt", "server.key")) -} - -func newApp() *iris.Application { - app := iris.New() - // app.Configure(iris.WithLowercaseRouting) // OPTIONAL. - - app.Get("/", func(ctx iris.Context) { - ctx.HTML("

Index Page

") - }) - - ctrl := &myController{} - // Register gRPC server. - grpcServer := grpc.NewServer() - pb.RegisterGreeterServer(grpcServer, ctrl) - - // serviceName := pb.File_helloworld_proto.Services().Get(0).FullName() - - // Register MVC application controller for gRPC services. - // You can bind as many mvc gRpc services in the same Party or app, - // as the ServiceName differs. - mvc.New(app).Handle(ctrl, mvc.GRPC{ - Server: grpcServer, // Required. - ServiceName: "helloworld.Greeter", // Required. - Strict: false, - }) - - return app -} - -type myController struct { - // Ctx iris.Context -} - -// SayHello implements helloworld.GreeterServer. -// See https://github.com/kataras/iris/issues/1449#issuecomment-625570442 -// for the comments below (https://github.com/iris-contrib/swagger). -// -// @Description greet service -// @Accept json -// @Produce json -// @Success 200 {string} string "Hello {name}" -// @Router /helloworld.Greeter/SayHello [post] -func (c *myController) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { - return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil -} diff --git a/_examples/mvc/grpc-compatible/main_test.go b/_examples/mvc/grpc-compatible/main_test.go deleted file mode 100644 index f937b9d997..0000000000 --- a/_examples/mvc/grpc-compatible/main_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestGRPCCompatible(t *testing.T) { - app := newApp() - - e := httptest.New(t, app) - e.POST("/helloworld.Greeter/SayHello").WithJSON(map[string]string{"name": "makis"}).Expect(). - Status(httptest.StatusOK). - JSON().Equal(map[string]string{"message": "Hello makis"}) -} diff --git a/_examples/mvc/grpc-compatible/server.crt b/_examples/mvc/grpc-compatible/server.crt deleted file mode 100644 index c554e9b174..0000000000 --- a/_examples/mvc/grpc-compatible/server.crt +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDkzCCAnugAwIBAgIUZLvkZXRRB1CP8FEjpRslVNRW4HwwDQYJKoZIhvcNAQEL -BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM -GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X -DTIwMDMwNzEwNDk0N1oXDTMwMDMwNTEwNDk0N1owWTELMAkGA1UEBhMCQVUxEzAR -BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 -IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEA1M0frpMcALlH2BGNNXdUlH3wmJSe9IUkfu/Il5/8SWuMWed39gL0 -Ps70TLi2cklyu5ZDuos6VRQecrkhtPWqcvM67YGM1unIJDVJJJZaiEMTLCv+1srE -+6DZBPZ3vrtA1Z3GH0xzDGyGyTNeQ+yRjdZkjYlalFkswi83qQzbwMx9bba76Tmg -ojfwMbYeoXMmQsIeKuCtpHNYo1uY1fIKnBa9CjOaJfshI+ch9YcFuntRYYS/UrXe -6XTnweFFN4MsSjkxlu7AImT2xW56y9Z64CYLwmT4MDB80FcKS/eRpCyG+KILoZXb -Jj8weneyG0An6gsAkZuN0I6M+XlXxYCzgQIDAQABo1MwUTAdBgNVHQ4EFgQUZLMV -958+SxbRbS3Yjy/9Yyo+FYAwHwYDVR0jBBgwFoAUZLMV958+SxbRbS3Yjy/9Yyo+ -FYAwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEANNSkFO6bxt8f -b7ZoNXYdBBopwx6o9e8uMLlWP1Wv3l0AWdn5WBlTj5ZoS90lKPEvLKO+BI096fwc -lC1P073k3mbXz0fQ/zyyz+7xn6h5FCe66LXU8AhpewolkIogSGwx4NT7lwIYpJ8T -BQXRtNtP3dH9w8iOpFECQIcLCMBamM1UGithrZRNmzYomFMnQYUz7A41eyXyTZ80 -IE5NbwVmd2XYpwrfqror6wkE5Psp3Fb7flKgMT1bU1ugmojIQEmFAYSUTSI7aNmT -W1XenU5tvCmSUnUgUW/vxj8u6W5SP5Lr0jirKT6WUON8lo5C/uWjzkX5Ij6J63JI -w0xukwlSLg== ------END CERTIFICATE----- diff --git a/_examples/mvc/grpc-compatible/server.key b/_examples/mvc/grpc-compatible/server.key deleted file mode 100644 index af79c0d121..0000000000 --- a/_examples/mvc/grpc-compatible/server.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA1M0frpMcALlH2BGNNXdUlH3wmJSe9IUkfu/Il5/8SWuMWed3 -9gL0Ps70TLi2cklyu5ZDuos6VRQecrkhtPWqcvM67YGM1unIJDVJJJZaiEMTLCv+ -1srE+6DZBPZ3vrtA1Z3GH0xzDGyGyTNeQ+yRjdZkjYlalFkswi83qQzbwMx9bba7 -6TmgojfwMbYeoXMmQsIeKuCtpHNYo1uY1fIKnBa9CjOaJfshI+ch9YcFuntRYYS/ -UrXe6XTnweFFN4MsSjkxlu7AImT2xW56y9Z64CYLwmT4MDB80FcKS/eRpCyG+KIL -oZXbJj8weneyG0An6gsAkZuN0I6M+XlXxYCzgQIDAQABAoIBAQCY12d8/L4tKuaW -Iy0YDMhcCwSmooB0wbhPz6t0c/1BQpTA8gZwVPjWr9A51qV7+pMEds3Yiy1vdA7W -eW/jSFuPSnG1qsnchncwwnDxbWhC3GJF5KeZ4HORA5s7/EZPYLEVfMeTLVaowr4g -ftuiz6RPioAYRGIhkNcb9cv1iH3BwA7B5D1T+qnOcxgrc4pK5Ecle9NyImU5q002 -vMh3zGuHz+H4zY66oOTplQ5gDONXkEGXCKIKczeFUMUiTVhQwEn/MYHmc+Gm2UVE -S29Is4/DdxCt3x2CRdC/2tsUK20azT51vzKs6qZ/Zvu4smOeov7KJ3Yedye62Dvx -SMrnVwJRAoGBAOovi0Mq1JYPUFBztSza9IiaKXgf9KVE34fPa41aSiLsqQIoiXel -My/LXmrjAKtUiusqUscC+KD+PvR926ceEyObE0lqYuE5MH3XYn25KF35RibD3qhv -Fd57GrgXVuTuB7Rqi8bvmgq/aTSSeFjNF1grojGvSHOb/i1LW8+9jiO1AoGBAOif -o+ndgkkpGloQv34Lko8YZyySaLoQDSrtyCmcE3DGTRVTtuVxZbU8ZRp726km6dQg -w+XbnlF07j7S3S2jvkIx1a/q3DhOe50uMlgEMUtvLtcgTKeqxBL0WWg1wZDWZI5R -Km1JzNl9QxN5MOMkD7XHdTZLO8wDwhr7ggp9xQgdAoGAXMOIfoqwuuBGCBhNY/83 -bgTi+FpwJicqBDn9eHiTdVIZgGleSq59oCkGtYBF+5f5jz2snHho1BziAyb3ozMe -kbQT57jkgiKNOsvej76QZukfCKv9vuqB1yccZl+YZAaFtsmdpKe0dR1tJw397e8P -mDVwgg3UvTbFcFuxepCzhSECgYBl/rEA9nWMnHAIc3Vyz6IoCbwbJ+qxQh5z5yQv -UlaXyOq9YiI8QdcleuCdlxb9+KjnsKIUI5IsBwf7FbuSqkvefDlEU8bRDWBXz4yV -WOlAOPShsHDLxy0HXonhDkjbt5UJbX8bwhMGfUyuuiF8RjB5NIYpEx2Z2z/9Uq92 -6CQ+5QKBgFZemvsuHfuAmEeHS0YfEs47IJIC1GtzfmJXT6w05wPKhMQbB+89HBlJ -Qbn1fG8B3hye6VCRAXk7B7w/FIPCeGUu4TQpoBzmtkH9LC8q09CPCxDISY64MQ1a -fJ2xUWbJbEQv799eTyxIXMG7+L+WYbKrZaiQzB2G/36ID1h7wCXi ------END RSA PRIVATE KEY----- diff --git a/_examples/mvc/hello-world/main.go b/_examples/mvc/hello-world/main.go deleted file mode 100644 index 10a4797c42..0000000000 --- a/_examples/mvc/hello-world/main.go +++ /dev/null @@ -1,154 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" - - "github.com/kataras/iris/v12/middleware/logger" - "github.com/kataras/iris/v12/middleware/recover" -) - -// This example is equivalent to the -// https://github.com/kataras/iris/blob/master/_examples/hello-world/main.go -// -// It seems that additional code you -// have to write doesn't worth it -// but remember that, this example -// does not make use of iris mvc features like -// the Model, Persistence or the View engine neither the Session, -// it's very simple for learning purposes, -// probably you'll never use such -// as simple controller anywhere in your app. -// -// The cost we have on this example for using MVC -// on the "/hello" path which serves JSON -// is ~2MB per 20MB throughput on my personal laptop, -// it's tolerated for the majority of the applications -// but you can choose -// what suits you best with Iris, low-level handlers: performance -// or high-level controllers: easier to maintain and smaller codebase on large applications. - -// Of course you can put all these to main func, it's just a separate function -// for the main_test.go. -func newApp() *iris.Application { - app := iris.New() - // Optionally, add two builtin handlers - // that can recover from any http-relative panics - // and log the requests to the terminal. - app.Use(recover.New()) - app.Use(logger.New()) - - // Serve a controller based on the root Router, "/". - mvc.New(app).Handle(new(ExampleController)) - return app -} - -func main() { - app := newApp() - - // http://localhost:8080 - // http://localhost:8080/ping - // http://localhost:8080/hello - // http://localhost:8080/custom_path - app.Listen(":8080") -} - -// ExampleController serves the "/", "/ping" and "/hello". -type ExampleController struct{} - -// Get serves -// Method: GET -// Resource: http://localhost:8080 -func (c *ExampleController) Get() mvc.Result { - return mvc.Response{ - ContentType: "text/html", - Text: "

Welcome

", - } -} - -// GetPing serves -// Method: GET -// Resource: http://localhost:8080/ping -func (c *ExampleController) GetPing() string { - return "pong" -} - -// GetHello serves -// Method: GET -// Resource: http://localhost:8080/hello -func (c *ExampleController) GetHello() interface{} { - return map[string]string{"message": "Hello Iris!"} -} - -// BeforeActivation called once, before the controller adapted to the main application -// and of course before the server ran. -// After version 9 you can also add custom routes for a specific controller's methods. -// Here you can register custom method's handlers -// use the standard router with `ca.Router` to do something that you can do without mvc as well, -// and add dependencies that will be binded to a controller's fields or method function's input arguments. -func (c *ExampleController) BeforeActivation(b mvc.BeforeActivation) { - anyMiddlewareHere := func(ctx iris.Context) { - ctx.Application().Logger().Warnf("Inside /custom_path") - ctx.Next() - } - b.Handle("GET", "/custom_path", "CustomHandlerWithoutFollowingTheNamingGuide", anyMiddlewareHere) - - // or even add a global middleware based on this controller's router, - // which in this example is the root "/": - // b.Router().Use(myMiddleware) -} - -// CustomHandlerWithoutFollowingTheNamingGuide serves -// Method: GET -// Resource: http://localhost:8080/custom_path -func (c *ExampleController) CustomHandlerWithoutFollowingTheNamingGuide() string { - return "hello from the custom handler without following the naming guide" -} - -// GetUserBy serves -// Method: GET -// Resource: http://localhost:8080/user/{username:string} -// By is a reserved "keyword" to tell the framework that you're going to -// bind path parameters in the function's input arguments, and it also -// helps to have "Get" and "GetBy" in the same controller. -// -// func (c *ExampleController) GetUserBy(username string) mvc.Result { -// return mvc.View{ -// Name: "user/username.html", -// Data: username, -// } -// } - -/* Can use more than one, the factory will make sure -that the correct http methods are being registered for each route -for this controller, uncomment these if you want: - -func (c *ExampleController) Post() {} -func (c *ExampleController) Put() {} -func (c *ExampleController) Delete() {} -func (c *ExampleController) Connect() {} -func (c *ExampleController) Head() {} -func (c *ExampleController) Patch() {} -func (c *ExampleController) Options() {} -func (c *ExampleController) Trace() {} -*/ - -/* -func (c *ExampleController) All() {} -// OR -func (c *ExampleController) Any() {} - - - -func (c *ExampleController) BeforeActivation(b mvc.BeforeActivation) { - // 1 -> the HTTP Method - // 2 -> the route's path - // 3 -> this controller's method name that should be handler for that route. - b.Handle("GET", "/mypath/{param}", "DoIt", optionalMiddlewareHere...) -} - -// After activation, all dependencies are set-ed - so read only access on them -// but still possible to add custom controller or simple standard handlers. -func (c *ExampleController) AfterActivation(a mvc.AfterActivation) {} - -*/ diff --git a/_examples/mvc/hello-world/main_test.go b/_examples/mvc/hello-world/main_test.go deleted file mode 100644 index 5fe2d36805..0000000000 --- a/_examples/mvc/hello-world/main_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestMVCHelloWorld(t *testing.T) { - e := httptest.New(t, newApp()) - - e.GET("/").Expect().Status(httptest.StatusOK). - ContentType("text/html", "utf-8").Body().Equal("

Welcome

") - - e.GET("/ping").Expect().Status(httptest.StatusOK). - Body().Equal("pong") - - e.GET("/hello").Expect().Status(httptest.StatusOK). - JSON().Object().Value("message").Equal("Hello Iris!") - - e.GET("/custom_path").Expect().Status(httptest.StatusOK). - Body().Equal("hello from the custom handler without following the naming guide") -} diff --git a/_examples/mvc/login-mvc-single-responsibility/folder_structure.png b/_examples/mvc/login-mvc-single-responsibility/folder_structure.png deleted file mode 100644 index 955e436e83..0000000000 Binary files a/_examples/mvc/login-mvc-single-responsibility/folder_structure.png and /dev/null differ diff --git a/_examples/mvc/login-mvc-single-responsibility/main.go b/_examples/mvc/login-mvc-single-responsibility/main.go deleted file mode 100644 index c64a120fc2..0000000000 --- a/_examples/mvc/login-mvc-single-responsibility/main.go +++ /dev/null @@ -1,56 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12/_examples/mvc/login-mvc-single-responsibility/user" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" - "github.com/kataras/iris/v12/sessions" -) - -func main() { - app := iris.New() - // You got full debug messages, useful when using MVC and you want to make - // sure that your code is aligned with the Iris' MVC Architecture. - app.Logger().SetLevel("debug") - - app.RegisterView(iris.HTML("./views", ".html").Layout("shared/layout.html")) - - app.HandleDir("/public", iris.Dir("./public")) - - userRouter := app.Party("/user") - { - manager := sessions.New(sessions.Config{ - Cookie: "sessioncookiename", - Expires: 24 * time.Hour, - }) - userRouter.Use(manager.Handler()) - mvc.Configure(userRouter, configureUserMVC) - } - - // http://localhost:8080/user/register - // http://localhost:8080/user/login - // http://localhost:8080/user/me - // http://localhost:8080/user/logout - // http://localhost:8080/user/1 - app.Listen(":8080", configure) -} - -func configureUserMVC(userApp *mvc.Application) { - userApp.Register( - user.NewDataSource(), - ) - userApp.Handle(new(user.Controller)) -} - -func configure(app *iris.Application) { - app.Configure( - iris.WithOptimizations, - iris.WithFireMethodNotAllowed, - iris.WithLowercaseRouting, - iris.WithPathIntelligence, - iris.WithTunneling, - ) -} diff --git a/_examples/mvc/login-mvc-single-responsibility/public/css/site.css b/_examples/mvc/login-mvc-single-responsibility/public/css/site.css deleted file mode 100644 index 163af622ad..0000000000 --- a/_examples/mvc/login-mvc-single-responsibility/public/css/site.css +++ /dev/null @@ -1,61 +0,0 @@ -/* Bordered form */ -form { - border: 3px solid #f1f1f1; -} - -/* Full-width inputs */ -input[type=text], input[type=password] { - width: 100%; - padding: 12px 20px; - margin: 8px 0; - display: inline-block; - border: 1px solid #ccc; - box-sizing: border-box; -} - -/* Set a style for all buttons */ -button { - background-color: #4CAF50; - color: white; - padding: 14px 20px; - margin: 8px 0; - border: none; - cursor: pointer; - width: 100%; -} - -/* Add a hover effect for buttons */ -button:hover { - opacity: 0.8; -} - -/* Extra style for the cancel button (red) */ -.cancelbtn { - width: auto; - padding: 10px 18px; - background-color: #f44336; -} - -/* Center the container */ - -/* Add padding to containers */ -.container { - padding: 16px; -} - -/* The "Forgot password" text */ -span.psw { - float: right; - padding-top: 16px; -} - -/* Change styles for span and cancel button on extra small screens */ -@media screen and (max-width: 300px) { - span.psw { - display: block; - float: none; - } - .cancelbtn { - width: 100%; - } -} \ No newline at end of file diff --git a/_examples/mvc/login-mvc-single-responsibility/user/auth.go b/_examples/mvc/login-mvc-single-responsibility/user/auth.go deleted file mode 100644 index 64ded5df13..0000000000 --- a/_examples/mvc/login-mvc-single-responsibility/user/auth.go +++ /dev/null @@ -1,110 +0,0 @@ -package user - -import ( - "errors" - "strconv" - "strings" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" - "github.com/kataras/iris/v12/sessions" -) - -const sessionIDKey = "UserID" - -// paths -var ( - PathLogin = mvc.Response{Path: "/user/login"} - PathLogout = mvc.Response{Path: "/user/logout"} -) - -// AuthController is the user authentication controller, a custom shared controller. -type AuthController struct { - // context is auto-binded if struct depends on this, - // in this controller we don't we do everything with mvc-style, - // and that's neither the 30% of its features. - // Ctx iris.Context - - Source *DataSource - Session *sessions.Session - - // the whole controller is request-scoped because we already depend on Session, so - // this will be new for each new incoming request, BeginRequest sets that based on the session. - UserID int64 -} - -// BeginRequest saves login state to the context, the user id. -func (c *AuthController) BeginRequest(ctx iris.Context) { - c.UserID, _ = c.Session.GetInt64(sessionIDKey) -} - -// EndRequest is here just to complete the BaseController -// in order to be tell iris to call the `BeginRequest` before the main method. -func (c *AuthController) EndRequest(ctx iris.Context) {} - -func (c *AuthController) fireError(err error) mvc.View { - return mvc.View{ - Code: iris.StatusBadRequest, - Name: "shared/error.html", - Data: iris.Map{"Title": "User Error", "Message": strings.ToUpper(err.Error())}, - } -} - -func (c *AuthController) redirectTo(id int64) mvc.Response { - return mvc.Response{Path: "/user/" + strconv.Itoa(int(id))} -} - -func (c *AuthController) createOrUpdate(firstname, username, password string) (user Model, err error) { - username = strings.Trim(username, " ") - if username == "" || password == "" || firstname == "" { - return user, errors.New("empty firstname, username or/and password") - } - - userToInsert := Model{ - Firstname: firstname, - Username: username, - password: password, - } // password is hashed by the Source. - - newUser, err := c.Source.InsertOrUpdate(userToInsert) - if err != nil { - return user, err - } - - return newUser, nil -} - -func (c *AuthController) isLoggedIn() bool { - // we don't search by session, we have the user id - // already by the `BeginRequest` middleware. - return c.UserID > 0 -} - -func (c *AuthController) verify(username, password string) (user Model, err error) { - if username == "" || password == "" { - return user, errors.New("please fill both username and password fields") - } - - u, found := c.Source.GetByUsername(username) - if !found { - // if user found with that username not found at all. - return user, errors.New("user with that username does not exist") - } - - if ok, err := ValidatePassword(password, u.HashedPassword); err != nil || !ok { - // if user found but an error occurred or the password is not valid. - return user, errors.New("please try to login with valid credentials") - } - - return u, nil -} - -// if logged in then destroy the session -// and redirect to the login page -// otherwise redirect to the registration page. -func (c *AuthController) logout() mvc.Response { - if c.isLoggedIn() { - c.Session.Destroy() - } - return PathLogin -} diff --git a/_examples/mvc/login-mvc-single-responsibility/user/controller.go b/_examples/mvc/login-mvc-single-responsibility/user/controller.go deleted file mode 100644 index b4777e4b7d..0000000000 --- a/_examples/mvc/login-mvc-single-responsibility/user/controller.go +++ /dev/null @@ -1,189 +0,0 @@ -package user - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" -) - -var ( - // About Code: iris.StatusSeeOther -> - // When redirecting from POST to GET request you -should- use this HTTP status code, - // however there're some (complicated) alternatives if you - // search online or even the HTTP RFC. - // "See Other" RFC 7231 - pathMyProfile = mvc.Response{Path: "/user/me", Code: iris.StatusSeeOther} - pathRegister = mvc.Response{Path: "/user/register"} -) - -// Controller is responsible to handle the following requests: -// GET /user/register -// POST /user/register -// GET /user/login -// POST /user/login -// GET /user/me -// GET /user/{id:int64} -// All HTTP Methods /user/logout -type Controller struct { - AuthController -} - -type formValue func(string) string - -// BeforeActivation called once before the server start -// and before the controller's registration, here you can add -// dependencies, to this controller and only, that the main caller may skip. -func (c *Controller) BeforeActivation(b mvc.BeforeActivation) { - // bind the context's `FormValue` as well in order to be - // acceptable on the controller or its methods' input arguments (NEW feature as well). - b.Dependencies().Register(func(ctx iris.Context) formValue { return ctx.FormValue }) -} - -type page struct { - Title string -} - -// GetRegister handles GET:/user/register. -// mvc.Result can accept any struct which contains a `Dispatch(ctx iris.Context)` method. -// Both mvc.Response and mvc.View are mvc.Result. -func (c *Controller) GetRegister() mvc.Result { - if c.isLoggedIn() { - return c.logout() - } - - // You could just use it as a variable to win some time in serve-time, - // this is an exersise for you :) - return mvc.View{ - Name: pathRegister.Path + ".html", - Data: page{"User Registration"}, - } -} - -// PostRegister handles POST:/user/register. -func (c *Controller) PostRegister(form formValue) mvc.Result { - // we can either use the `c.Ctx.ReadForm` or read values one by one. - var ( - firstname = form("firstname") - username = form("username") - password = form("password") - ) - - user, err := c.createOrUpdate(firstname, username, password) - if err != nil { - return c.fireError(err) - } - - // setting a session value was never easier. - c.Session.Set(sessionIDKey, user.ID) - // succeed, nothing more to do here, just redirect to the /user/me. - return pathMyProfile -} - -// with these static views, -// you can use variables-- that are initialized before server start -// so you can win some time on serving. -// You can do it else where as well but I let them as pracise for you, -// essentially you can understand by just looking below. -var userLoginView = mvc.View{ - Name: PathLogin.Path + ".html", - Data: page{"User Login"}, -} - -// GetLogin handles GET:/user/login. -func (c *Controller) GetLogin() mvc.Result { - if c.isLoggedIn() { - return c.logout() - } - return userLoginView -} - -// PostLogin handles POST:/user/login. -func (c *Controller) PostLogin(form formValue) mvc.Result { - var ( - username = form("username") - password = form("password") - ) - - user, err := c.verify(username, password) - if err != nil { - return c.fireError(err) - } - - c.Session.Set(sessionIDKey, user.ID) - return pathMyProfile -} - -// AnyLogout handles any method on path /user/logout. -func (c *Controller) AnyLogout() { - c.logout() -} - -// GetMe handles GET:/user/me. -func (c *Controller) GetMe() mvc.Result { - id, err := c.Session.GetInt64(sessionIDKey) - if err != nil || id <= 0 { - // when not already logged in, redirect to login. - return PathLogin - } - - u, found := c.Source.GetByID(id) - if !found { - // if the session exists but for some reason the user doesn't exist in the "database" - // then logout him and redirect to the register page. - return c.logout() - } - - // set the model and render the view template. - return mvc.View{ - Name: pathMyProfile.Path + ".html", - Data: iris.Map{ - "Title": "Profile of " + u.Username, - "User": u, - }, - } -} - -func (c *Controller) renderNotFound(id int64) mvc.View { - return mvc.View{ - Code: iris.StatusNotFound, - Name: "user/notfound.html", - Data: iris.Map{ - "Title": "User Not Found", - "ID": id, - }, - } -} - -// Dispatch completes the `mvc.Result` interface -// in order to be able to return a type of `Model` -// as mvc.Result. -// If this function didn't exist then -// we should explicit set the output result to that Model or to an interface{}. -func (u Model) Dispatch(ctx iris.Context) { - ctx.JSON(u) -} - -// GetBy handles GET:/user/{id:int64}, -// i.e http://localhost:8080/user/1 -func (c *Controller) GetBy(userID int64) mvc.Result { - // we have /user/{id} - // fetch and render user json. - user, found := c.Source.GetByID(userID) - if !found { - // not user found with that ID. - return c.renderNotFound(userID) - } - - // Q: how the hell Model can be return as mvc.Result? - // A: I told you before on some comments and the docs, - // any struct that has a `Dispatch(ctx iris.Context)` - // can be returned as an mvc.Result(see ~20 lines above), - // therefore we are able to combine many type of results in the same method. - // For example, here, we return either an mvc.View to render a not found custom template - // either a user which returns the Model as JSON via its Dispatch. - // - // We could also return just a struct value that is not an mvc.Result, - // if the output result of the `GetBy` was that struct's type or an interface{} - // and iris would render that with JSON as well, but here we can't do that without complete the `Dispatch` - // function, because we may return an mvc.View which is an mvc.Result. - return user -} diff --git a/_examples/mvc/login-mvc-single-responsibility/user/datasource.go b/_examples/mvc/login-mvc-single-responsibility/user/datasource.go deleted file mode 100644 index ad596e214e..0000000000 --- a/_examples/mvc/login-mvc-single-responsibility/user/datasource.go +++ /dev/null @@ -1,114 +0,0 @@ -package user - -import ( - "errors" - "sync" - "time" -) - -// IDGenerator would be our user ID generator -// but here we keep the order of users by their IDs -// so we will use numbers that can be easly written -// to the browser to get results back from the REST API. -// var IDGenerator = func() string { -// return uuid.NewV4().String() -// } - -// DataSource is our data store example. -type DataSource struct { - Users map[int64]Model - mu sync.RWMutex -} - -// NewDataSource returns a new user data source. -func NewDataSource() *DataSource { - return &DataSource{ - Users: make(map[int64]Model), - } -} - -// GetBy receives a query function -// which is fired for every single user model inside -// our imaginary database. -// When that function returns true then it stops the iteration. -// -// It returns the query's return last known boolean value -// and the last known user model -// to help callers to reduce the loc. -// -// But be carefully, the caller should always check for the "found" -// because it may be false but the user model has actually real data inside it. -// -// It's actually a simple but very clever prototype function -// I'm think of and using everywhere since then, -// hope you find it very useful too. -func (d *DataSource) GetBy(query func(Model) bool) (user Model, found bool) { - d.mu.RLock() - for _, user = range d.Users { - found = query(user) - if found { - break - } - } - d.mu.RUnlock() - return -} - -// GetByID returns a user model based on its ID. -func (d *DataSource) GetByID(id int64) (Model, bool) { - return d.GetBy(func(u Model) bool { - return u.ID == id - }) -} - -// GetByUsername returns a user model based on the Username. -func (d *DataSource) GetByUsername(username string) (Model, bool) { - return d.GetBy(func(u Model) bool { - return u.Username == username - }) -} - -func (d *DataSource) getLastID() (lastID int64) { - d.mu.RLock() - for id := range d.Users { - if id > lastID { - lastID = id - } - } - d.mu.RUnlock() - - return lastID -} - -// InsertOrUpdate adds or updates a user to the (memory) storage. -func (d *DataSource) InsertOrUpdate(user Model) (Model, error) { - // no matter what we will update the password hash - // for both update and insert actions. - hashedPassword, err := GeneratePassword(user.password) - if err != nil { - return user, err - } - user.HashedPassword = hashedPassword - - // update - if id := user.ID; id > 0 { - _, found := d.GetByID(id) - if !found { - return user, errors.New("ID should be zero or a valid one that maps to an existing User") - } - d.mu.Lock() - d.Users[id] = user - d.mu.Unlock() - return user, nil - } - - // insert - id := d.getLastID() + 1 - user.ID = id - d.mu.Lock() - user.CreatedAt = time.Now() - d.Users[id] = user - d.mu.Unlock() - - return user, nil -} diff --git a/_examples/mvc/login-mvc-single-responsibility/user/model.go b/_examples/mvc/login-mvc-single-responsibility/user/model.go deleted file mode 100644 index c49c462ab1..0000000000 --- a/_examples/mvc/login-mvc-single-responsibility/user/model.go +++ /dev/null @@ -1,36 +0,0 @@ -package user - -import ( - "time" - - "golang.org/x/crypto/bcrypt" -) - -// Model is our User example model. -type Model struct { - ID int64 `json:"id"` - Firstname string `json:"firstname"` - Username string `json:"username"` - // password is the client-given password - // which will not be stored anywhere in the server. - // It's here only for actions like registration and update password, - // because we caccept a Model instance - // inside the `DataSource#InsertOrUpdate` function. - password string - HashedPassword []byte `json:"-"` - CreatedAt time.Time `json:"created_at"` -} - -// GeneratePassword will generate a hashed password for us based on the -// user's input. -func GeneratePassword(userPassword string) ([]byte, error) { - return bcrypt.GenerateFromPassword([]byte(userPassword), bcrypt.DefaultCost) -} - -// ValidatePassword will check if passwords are matched. -func ValidatePassword(userPassword string, hashed []byte) (bool, error) { - if err := bcrypt.CompareHashAndPassword(hashed, []byte(userPassword)); err != nil { - return false, err - } - return true, nil -} diff --git a/_examples/mvc/login-mvc-single-responsibility/views/shared/error.html b/_examples/mvc/login-mvc-single-responsibility/views/shared/error.html deleted file mode 100644 index aca44bcc24..0000000000 --- a/_examples/mvc/login-mvc-single-responsibility/views/shared/error.html +++ /dev/null @@ -1,4 +0,0 @@ -

Error.

-

An error occurred while processing your request.

- -

{{.Message}}

\ No newline at end of file diff --git a/_examples/mvc/login-mvc-single-responsibility/views/shared/layout.html b/_examples/mvc/login-mvc-single-responsibility/views/shared/layout.html deleted file mode 100644 index 1e02677b07..0000000000 --- a/_examples/mvc/login-mvc-single-responsibility/views/shared/layout.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - {{.Title}} - - - - - {{ yield }} - - - \ No newline at end of file diff --git a/_examples/mvc/login-mvc-single-responsibility/views/user/login.html b/_examples/mvc/login-mvc-single-responsibility/views/user/login.html deleted file mode 100644 index 132c739eb6..0000000000 --- a/_examples/mvc/login-mvc-single-responsibility/views/user/login.html +++ /dev/null @@ -1,11 +0,0 @@ -
-
- - - - - - - -
-
\ No newline at end of file diff --git a/_examples/mvc/login-mvc-single-responsibility/views/user/me.html b/_examples/mvc/login-mvc-single-responsibility/views/user/me.html deleted file mode 100644 index 17e2f0b367..0000000000 --- a/_examples/mvc/login-mvc-single-responsibility/views/user/me.html +++ /dev/null @@ -1,3 +0,0 @@ -

- Welcome back {{.User.Firstname}}! -

\ No newline at end of file diff --git a/_examples/mvc/login-mvc-single-responsibility/views/user/notfound.html b/_examples/mvc/login-mvc-single-responsibility/views/user/notfound.html deleted file mode 100644 index c0000bda28..0000000000 --- a/_examples/mvc/login-mvc-single-responsibility/views/user/notfound.html +++ /dev/null @@ -1,3 +0,0 @@ -

- User with ID {{.ID}} does not exist. -

\ No newline at end of file diff --git a/_examples/mvc/login-mvc-single-responsibility/views/user/register.html b/_examples/mvc/login-mvc-single-responsibility/views/user/register.html deleted file mode 100644 index 79684d9981..0000000000 --- a/_examples/mvc/login-mvc-single-responsibility/views/user/register.html +++ /dev/null @@ -1,14 +0,0 @@ -
-
- - - - - - - - - - -
-
\ No newline at end of file diff --git a/_examples/mvc/login/datamodels/user.go b/_examples/mvc/login/datamodels/user.go deleted file mode 100644 index fdcb63f9d0..0000000000 --- a/_examples/mvc/login/datamodels/user.go +++ /dev/null @@ -1,41 +0,0 @@ -package datamodels - -import ( - "time" - - "golang.org/x/crypto/bcrypt" -) - -// User is our User example model. -// Keep note that the tags for public-use (for our web app) -// should be kept in other file like "web/viewmodels/user.go" -// which could wrap by embedding the datamodels.User or -// define completely new fields instead but for the sake -// of the example, we will use this datamodel -// as the only one User model in our application. -type User struct { - ID int64 `json:"id" form:"id"` - Firstname string `json:"firstname" form:"firstname"` - Username string `json:"username" form:"username"` - HashedPassword []byte `json:"-" form:"-"` - CreatedAt time.Time `json:"created_at" form:"created_at"` -} - -// IsValid can do some very very simple "low-level" data validations. -func (u User) IsValid() bool { - return u.ID > 0 -} - -// GeneratePassword will generate a hashed password for us based on the -// user's input. -func GeneratePassword(userPassword string) ([]byte, error) { - return bcrypt.GenerateFromPassword([]byte(userPassword), bcrypt.DefaultCost) -} - -// ValidatePassword will check if passwords are matched. -func ValidatePassword(userPassword string, hashed []byte) (bool, error) { - if err := bcrypt.CompareHashAndPassword(hashed, []byte(userPassword)); err != nil { - return false, err - } - return true, nil -} diff --git a/_examples/mvc/login/datasource/users.go b/_examples/mvc/login/datasource/users.go deleted file mode 100644 index 1c3fb74af4..0000000000 --- a/_examples/mvc/login/datasource/users.go +++ /dev/null @@ -1,31 +0,0 @@ -// file: datasource/users.go - -package datasource - -import ( - "errors" - - "github.com/kataras/iris/v12/_examples/mvc/login/datamodels" -) - -// Engine is from where to fetch the data, in this case the users. -type Engine uint32 - -const ( - // Memory stands for simple memory location; - // map[int64] datamodels.User ready to use, it's our source in this example. - Memory Engine = iota - // Bolt for boltdb source location. - Bolt - // MySQL for mysql-compatible source location. - MySQL -) - -// LoadUsers returns all users(empty map) from the memory, for the sake of simplicty. -func LoadUsers(engine Engine) (map[int64]datamodels.User, error) { - if engine != Memory { - return nil, errors.New("for the sake of simplicity we're using a simple map as the data source") - } - - return make(map[int64]datamodels.User), nil -} diff --git a/_examples/mvc/login/folder_structure.png b/_examples/mvc/login/folder_structure.png deleted file mode 100644 index c89b273dd0..0000000000 Binary files a/_examples/mvc/login/folder_structure.png and /dev/null differ diff --git a/_examples/mvc/login/main.go b/_examples/mvc/login/main.go deleted file mode 100644 index 26f8f4aefb..0000000000 --- a/_examples/mvc/login/main.go +++ /dev/null @@ -1,83 +0,0 @@ -// file: main.go - -package main - -import ( - "time" - - "github.com/kataras/iris/v12/_examples/mvc/login/datasource" - "github.com/kataras/iris/v12/_examples/mvc/login/repositories" - "github.com/kataras/iris/v12/_examples/mvc/login/services" - "github.com/kataras/iris/v12/_examples/mvc/login/web/controllers" - "github.com/kataras/iris/v12/_examples/mvc/login/web/middleware" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" - "github.com/kataras/iris/v12/sessions" -) - -func main() { - app := iris.New() - // You got full debug messages, useful when using MVC and you want to make - // sure that your code is aligned with the Iris' MVC Architecture. - app.Logger().SetLevel("debug") - - // Load the template files. - tmpl := iris.HTML("./web/views", ".html"). - Layout("shared/layout.html"). - Reload(true) - app.RegisterView(tmpl) - - app.HandleDir("/public", iris.Dir("./web/public")) - - app.OnAnyErrorCode(func(ctx iris.Context) { - ctx.ViewData("Message", ctx.Values(). - GetStringDefault("message", "The page you're looking for doesn't exist")) - ctx.View("shared/error.html") - }) - - // ---- Serve our controllers. ---- - - // Prepare our repositories and services. - db, err := datasource.LoadUsers(datasource.Memory) - if err != nil { - app.Logger().Fatalf("error while loading the users: %v", err) - return - } - repo := repositories.NewUserRepository(db) - userService := services.NewUserService(repo) - - // "/users" based mvc application. - users := mvc.New(app.Party("/users")) - // Add the basic authentication(admin:password) middleware - // for the /users based requests. - users.Router.Use(middleware.BasicAuth) - // Bind the "userService" to the UserController's Service (interface) field. - users.Register(userService) - users.Handle(new(controllers.UsersController)) - - // "/user" based mvc application. - sessManager := sessions.New(sessions.Config{ - Cookie: "sessioncookiename", - Expires: 24 * time.Hour, - }) - user := mvc.New(app.Party("/user")) - user.Register( - userService, - sessManager.Start, - ) - user.Handle(new(controllers.UserController)) - - // http://localhost:8080/noexist - // and all controller's methods like - // http://localhost:8080/users/1 - // http://localhost:8080/user/register - // http://localhost:8080/user/login - // http://localhost:8080/user/me - // http://localhost:8080/user/logout - // basic auth: "admin", "password", see "./middleware/basicauth.go" source file. - - // Starts the web server at localhost:8080 - // Enables faster json serialization and more. - app.Listen(":8080", iris.WithOptimizations) -} diff --git a/_examples/mvc/login/repositories/user_repository.go b/_examples/mvc/login/repositories/user_repository.go deleted file mode 100644 index 71f9deff23..0000000000 --- a/_examples/mvc/login/repositories/user_repository.go +++ /dev/null @@ -1,174 +0,0 @@ -package repositories - -import ( - "errors" - "sync" - - "github.com/kataras/iris/v12/_examples/mvc/login/datamodels" -) - -// Query represents the visitor and action queries. -type Query func(datamodels.User) bool - -// UserRepository handles the basic operations of a user entity/model. -// It's an interface in order to be testable, i.e a memory user repository or -// a connected to an sql database. -type UserRepository interface { - Exec(query Query, action Query, limit int, mode int) (ok bool) - - Select(query Query) (user datamodels.User, found bool) - SelectMany(query Query, limit int) (results []datamodels.User) - - InsertOrUpdate(user datamodels.User) (updatedUser datamodels.User, err error) - Delete(query Query, limit int) (deleted bool) -} - -// NewUserRepository returns a new user memory-based repository, -// the one and only repository type in our example. -func NewUserRepository(source map[int64]datamodels.User) UserRepository { - return &userMemoryRepository{source: source} -} - -// userMemoryRepository is a "UserRepository" -// which manages the users using the memory data source (map). -type userMemoryRepository struct { - source map[int64]datamodels.User - mu sync.RWMutex -} - -const ( - // ReadOnlyMode will RLock(read) the data . - ReadOnlyMode = iota - // ReadWriteMode will Lock(read/write) the data. - ReadWriteMode -) - -func (r *userMemoryRepository) Exec(query Query, action Query, actionLimit int, mode int) (ok bool) { - loops := 0 - - if mode == ReadOnlyMode { - r.mu.RLock() - defer r.mu.RUnlock() - } else { - r.mu.Lock() - defer r.mu.Unlock() - } - - for _, user := range r.source { - ok = query(user) - if ok { - if action(user) { - loops++ - if actionLimit >= loops { - break // break - } - } - } - } - - return -} - -// Select receives a query function -// which is fired for every single user model inside -// our imaginary data source. -// When that function returns true then it stops the iteration. -// -// It returns the query's return last known boolean value -// and the last known user model -// to help callers to reduce the LOC. -// -// It's actually a simple but very clever prototype function -// I'm using everywhere since I firstly think of it, -// hope you'll find it very useful as well. -func (r *userMemoryRepository) Select(query Query) (user datamodels.User, found bool) { - found = r.Exec(query, func(m datamodels.User) bool { - user = m - return true - }, 1, ReadOnlyMode) - - // set an empty datamodels.User if not found at all. - if !found { - user = datamodels.User{} - } - - return -} - -// SelectMany same as Select but returns one or more datamodels.User as a slice. -// If limit <=0 then it returns everything. -func (r *userMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.User) { - r.Exec(query, func(m datamodels.User) bool { - results = append(results, m) - return true - }, limit, ReadOnlyMode) - - return -} - -// InsertOrUpdate adds or updates a user to the (memory) storage. -// -// Returns the new user and an error if any. -func (r *userMemoryRepository) InsertOrUpdate(user datamodels.User) (datamodels.User, error) { - id := user.ID - - if id == 0 { // Create new action - var lastID int64 - // find the biggest ID in order to not have duplications - // in productions apps you can use a third-party - // library to generate a UUID as string. - r.mu.RLock() - for _, item := range r.source { - if item.ID > lastID { - lastID = item.ID - } - } - r.mu.RUnlock() - - id = lastID + 1 - user.ID = id - - // map-specific thing - r.mu.Lock() - r.source[id] = user - r.mu.Unlock() - - return user, nil - } - - // Update action based on the user.ID, - // here we will allow updating the poster and genre if not empty. - // Alternatively we could do pure replace instead: - // r.source[id] = user - // and comment the code below; - current, exists := r.Select(func(m datamodels.User) bool { - return m.ID == id - }) - - if !exists { // ID is not a real one, return an error. - return datamodels.User{}, errors.New("failed to update a nonexistent user") - } - - // or comment these and r.source[id] = user for pure replace - if user.Username != "" { - current.Username = user.Username - } - - if user.Firstname != "" { - current.Firstname = user.Firstname - } - - // map-specific thing - r.mu.Lock() - r.source[id] = current - r.mu.Unlock() - - return user, nil -} - -func (r *userMemoryRepository) Delete(query Query, limit int) bool { - return r.Exec(query, func(m datamodels.User) bool { - delete(r.source, m.ID) - return true - }, limit, ReadWriteMode) -} diff --git a/_examples/mvc/login/services/user_service.go b/_examples/mvc/login/services/user_service.go deleted file mode 100644 index 4921c4f954..0000000000 --- a/_examples/mvc/login/services/user_service.go +++ /dev/null @@ -1,125 +0,0 @@ -package services - -import ( - "errors" - - "github.com/kataras/iris/v12/_examples/mvc/login/datamodels" - "github.com/kataras/iris/v12/_examples/mvc/login/repositories" -) - -// UserService handles CRUID operations of a user datamodel, -// it depends on a user repository for its actions. -// It's here to decouple the data source from the higher level compoments. -// As a result a different repository type can be used with the same logic without any aditional changes. -// It's an interface and it's used as interface everywhere -// because we may need to change or try an experimental different domain logic at the future. -type UserService interface { - GetAll() []datamodels.User - GetByID(id int64) (datamodels.User, bool) - GetByUsernameAndPassword(username, userPassword string) (datamodels.User, bool) - DeleteByID(id int64) bool - - Update(id int64, user datamodels.User) (datamodels.User, error) - UpdatePassword(id int64, newPassword string) (datamodels.User, error) - UpdateUsername(id int64, newUsername string) (datamodels.User, error) - - Create(userPassword string, user datamodels.User) (datamodels.User, error) -} - -// NewUserService returns the default user service. -func NewUserService(repo repositories.UserRepository) UserService { - return &userService{ - repo: repo, - } -} - -type userService struct { - repo repositories.UserRepository -} - -// GetAll returns all users. -func (s *userService) GetAll() []datamodels.User { - return s.repo.SelectMany(func(_ datamodels.User) bool { - return true - }, -1) -} - -// GetByID returns a user based on its id. -func (s *userService) GetByID(id int64) (datamodels.User, bool) { - return s.repo.Select(func(m datamodels.User) bool { - return m.ID == id - }) -} - -// GetByUsernameAndPassword returns a user based on its username and passowrd, -// used for authentication. -func (s *userService) GetByUsernameAndPassword(username, userPassword string) (datamodels.User, bool) { - if username == "" || userPassword == "" { - return datamodels.User{}, false - } - - return s.repo.Select(func(m datamodels.User) bool { - if m.Username == username { - hashed := m.HashedPassword - if ok, _ := datamodels.ValidatePassword(userPassword, hashed); ok { - return true - } - } - return false - }) -} - -// Update updates every field from an existing User, -// it's not safe to be used via public API, -// however we will use it on the web/controllers/user_controller.go#PutBy -// in order to show you how it works. -func (s *userService) Update(id int64, user datamodels.User) (datamodels.User, error) { - user.ID = id - return s.repo.InsertOrUpdate(user) -} - -// UpdatePassword updates a user's password. -func (s *userService) UpdatePassword(id int64, newPassword string) (datamodels.User, error) { - // update the user and return it. - hashed, err := datamodels.GeneratePassword(newPassword) - if err != nil { - return datamodels.User{}, err - } - - return s.Update(id, datamodels.User{ - HashedPassword: hashed, - }) -} - -// UpdateUsername updates a user's username. -func (s *userService) UpdateUsername(id int64, newUsername string) (datamodels.User, error) { - return s.Update(id, datamodels.User{ - Username: newUsername, - }) -} - -// Create inserts a new User, -// the userPassword is the client-typed password -// it will be hashed before the insertion to our repository. -func (s *userService) Create(userPassword string, user datamodels.User) (datamodels.User, error) { - if user.ID > 0 || userPassword == "" || user.Firstname == "" || user.Username == "" { - return datamodels.User{}, errors.New("unable to create this user") - } - - hashed, err := datamodels.GeneratePassword(userPassword) - if err != nil { - return datamodels.User{}, err - } - user.HashedPassword = hashed - - return s.repo.InsertOrUpdate(user) -} - -// DeleteByID deletes a user by its id. -// -// Returns true if deleted otherwise false. -func (s *userService) DeleteByID(id int64) bool { - return s.repo.Delete(func(m datamodels.User) bool { - return m.ID == id - }, 1) -} diff --git a/_examples/mvc/login/web/controllers/user_controller.go b/_examples/mvc/login/web/controllers/user_controller.go deleted file mode 100644 index 56686619cb..0000000000 --- a/_examples/mvc/login/web/controllers/user_controller.go +++ /dev/null @@ -1,170 +0,0 @@ -// file: controllers/user_controller.go - -package controllers - -import ( - "github.com/kataras/iris/v12/_examples/mvc/login/datamodels" - "github.com/kataras/iris/v12/_examples/mvc/login/services" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" - "github.com/kataras/iris/v12/sessions" -) - -// UserController is our /user controller. -// UserController is responsible to handle the following requests: -// GET /user/register -// POST /user/register -// GET /user/login -// POST /user/login -// GET /user/me -// All HTTP Methods /user/logout -type UserController struct { - // context is auto-binded by Iris on each request, - // remember that on each incoming request iris creates a new UserController each time, - // so all fields are request-scoped by-default, only dependency injection is able to set - // custom fields like the Service which is the same for all requests (static binding) - // and the Session which depends on the current context (dynamic binding). - Ctx iris.Context - - // Our UserService, it's an interface which - // is binded from the main application. - Service services.UserService - - // Session, binded using dependency injection from the main.go. - Session *sessions.Session -} - -const userIDKey = "UserID" - -func (c *UserController) getCurrentUserID() int64 { - userID := c.Session.GetInt64Default(userIDKey, 0) - return userID -} - -func (c *UserController) isLoggedIn() bool { - return c.getCurrentUserID() > 0 -} - -func (c *UserController) logout() { - c.Session.Destroy() -} - -var registerStaticView = mvc.View{ - Name: "user/register.html", - Data: iris.Map{"Title": "User Registration"}, -} - -// GetRegister handles GET: http://localhost:8080/user/register. -func (c *UserController) GetRegister() mvc.Result { - if c.isLoggedIn() { - c.logout() - } - - return registerStaticView -} - -// PostRegister handles POST: http://localhost:8080/user/register. -func (c *UserController) PostRegister() mvc.Result { - // get firstname, username and password from the form. - var ( - firstname = c.Ctx.FormValue("firstname") - username = c.Ctx.FormValue("username") - password = c.Ctx.FormValue("password") - ) - - // create the new user, the password will be hashed by the service. - u, err := c.Service.Create(password, datamodels.User{ - Username: username, - Firstname: firstname, - }) - - // set the user's id to this session even if err != nil, - // the zero id doesn't matters because .getCurrentUserID() checks for that. - // If err != nil then it will be shown, see below on mvc.Response.Err: err. - c.Session.Set(userIDKey, u.ID) - - return mvc.Response{ - // if not nil then this error will be shown instead. - Err: err, - // redirect to /user/me. - Path: "/user/me", - // When redirecting from POST to GET request you -should- use this HTTP status code, - // however there're some (complicated) alternatives if you - // search online or even the HTTP RFC. - // Status "See Other" RFC 7231, however iris can automatically fix that - // but it's good to know you can set a custom code; - // Code: 303, - } -} - -var loginStaticView = mvc.View{ - Name: "user/login.html", - Data: iris.Map{"Title": "User Login"}, -} - -// GetLogin handles GET: http://localhost:8080/user/login. -func (c *UserController) GetLogin() mvc.Result { - if c.isLoggedIn() { - // if it's already logged in then destroy the previous session. - c.logout() - } - - return loginStaticView -} - -// PostLogin handles POST: http://localhost:8080/user/register. -func (c *UserController) PostLogin() mvc.Result { - var ( - username = c.Ctx.FormValue("username") - password = c.Ctx.FormValue("password") - ) - - u, found := c.Service.GetByUsernameAndPassword(username, password) - - if !found { - return mvc.Response{ - Path: "/user/register", - } - } - - c.Session.Set(userIDKey, u.ID) - - return mvc.Response{ - Path: "/user/me", - } -} - -// GetMe handles GET: http://localhost:8080/user/me. -func (c *UserController) GetMe() mvc.Result { - if !c.isLoggedIn() { - // if it's not logged in then redirect user to the login page. - return mvc.Response{Path: "/user/login"} - } - - u, found := c.Service.GetByID(c.getCurrentUserID()) - if !found { - // if the session exists but for some reason the user doesn't exist in the "database" - // then logout and re-execute the function, it will redirect the client to the - // /user/login page. - c.logout() - return c.GetMe() - } - - return mvc.View{ - Name: "user/me.html", - Data: iris.Map{ - "Title": "Profile of " + u.Username, - "User": u, - }, - } -} - -// AnyLogout handles All/Any HTTP Methods for: http://localhost:8080/user/logout. -func (c *UserController) AnyLogout() { - if c.isLoggedIn() { - c.logout() - } - - c.Ctx.Redirect("/user/login") -} diff --git a/_examples/mvc/login/web/controllers/users_controller.go b/_examples/mvc/login/web/controllers/users_controller.go deleted file mode 100644 index 02715c4842..0000000000 --- a/_examples/mvc/login/web/controllers/users_controller.go +++ /dev/null @@ -1,88 +0,0 @@ -package controllers - -import ( - "github.com/kataras/iris/v12/_examples/mvc/login/datamodels" - "github.com/kataras/iris/v12/_examples/mvc/login/services" - - "github.com/kataras/iris/v12" -) - -// UsersController is our /users API controller. -// GET /users | get all -// GET /users/{id:int64} | get by id -// PUT /users/{id:int64} | update by id -// DELETE /users/{id:int64} | delete by id -// Requires basic authentication. -type UsersController struct { - // Optionally: context is auto-binded by Iris on each request, - // remember that on each incoming request iris creates a new UserController each time, - // so all fields are request-scoped by-default, only dependency injection is able to set - // custom fields like the Service which is the same for all requests (static binding). - Ctx iris.Context - - // Our UserService, it's an interface which - // is binded from the main application. - Service services.UserService -} - -// Get returns list of the users. -// Demo: -// curl -i -u admin:password http://localhost:8080/users -// -// The correct way if you have sensitive data: -// func (c *UsersController) Get() (results []viewmodels.User) { -// data := c.Service.GetAll() -// -// for _, user := range data { -// results = append(results, viewmodels.User{user}) -// } -// return -// } -// otherwise just return the datamodels. -func (c *UsersController) Get() (results []datamodels.User) { - return c.Service.GetAll() -} - -// GetBy returns a user. -// Demo: -// curl -i -u admin:password http://localhost:8080/users/1 -func (c *UsersController) GetBy(id int64) (user datamodels.User, found bool) { - u, found := c.Service.GetByID(id) - if !found { - // this message will be binded to the - // main.go -> app.OnAnyErrorCode -> NotFound -> shared/error.html -> .Message text. - c.Ctx.Values().Set("message", "User couldn't be found!") - } - return u, found // it will throw/emit 404 if found == false. -} - -// PutBy updates a user. -// Demo: -// curl -i -X PUT -u admin:password -F "username=kataras" -// -F "password=rawPasswordIsNotSafeIfOrNotHTTPs_You_Should_Use_A_client_side_lib_for_hash_as_well" -// http://localhost:8080/users/1 -func (c *UsersController) PutBy(id int64) (datamodels.User, error) { - // username := c.Ctx.FormValue("username") - // password := c.Ctx.FormValue("password") - u := datamodels.User{} - if err := c.Ctx.ReadForm(&u); err != nil { - return u, err - } - - return c.Service.Update(id, u) -} - -// DeleteBy deletes a user. -// Demo: -// curl -i -X DELETE -u admin:password http://localhost:8080/users/1 -func (c *UsersController) DeleteBy(id int64) interface{} { - wasDel := c.Service.DeleteByID(id) - if wasDel { - // return the deleted user's ID - return map[string]interface{}{"deleted": id} - } - // right here we can see that a method function - // can return any of those two types(map or int), - // we don't have to specify the return type to a specific type. - return iris.StatusBadRequest // same as 400. -} diff --git a/_examples/mvc/login/web/middleware/basicauth.go b/_examples/mvc/login/web/middleware/basicauth.go deleted file mode 100644 index eaf085ba96..0000000000 --- a/_examples/mvc/login/web/middleware/basicauth.go +++ /dev/null @@ -1,12 +0,0 @@ -// file: middleware/basicauth.go - -package middleware - -import "github.com/kataras/iris/v12/middleware/basicauth" - -// BasicAuth middleware sample. -var BasicAuth = basicauth.New(basicauth.Config{ - Users: map[string]string{ - "admin": "password", - }, -}) diff --git a/_examples/mvc/login/web/public/css/site.css b/_examples/mvc/login/web/public/css/site.css deleted file mode 100644 index d6dc3b109f..0000000000 --- a/_examples/mvc/login/web/public/css/site.css +++ /dev/null @@ -1,61 +0,0 @@ -/* Bordered form */ -form { - border: 3px solid #f1f1f1; -} - -/* Full-width inputs */ -input[type=text], input[type=password] { - width: 100%; - padding: 12px 20px; - margin: 8px 0; - display: inline-block; - border: 1px solid #ccc; - box-sizing: border-box; -} - -/* Set a style for all buttons */ -button { - background-color: #4CAF50; - color: white; - padding: 14px 20px; - margin: 8px 0; - border: none; - cursor: pointer; - width: 100%; -} - -/* Add a hover effect for buttons */ -button:hover { - opacity: 0.8; -} - -/* Extra style for the cancel button (red) */ -.cancelbtn { - width: auto; - padding: 10px 18px; - background-color: #f44336; -} - -/* Center the container */ - -/* Add padding to containers */ -.container { - padding: 16px; -} - -/* The "Forgot password" text */ -span.psw { - float: right; - padding-top: 16px; -} - -/* Change styles for span and cancel button on extra small screens */ -@media screen and (max-width: 300px) { - span.psw { - display: block; - float: none; - } - .cancelbtn { - width: 100%; - } -} \ No newline at end of file diff --git a/_examples/mvc/login/web/viewmodels/README.md b/_examples/mvc/login/web/viewmodels/README.md deleted file mode 100644 index 4cd5557774..0000000000 --- a/_examples/mvc/login/web/viewmodels/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# View Models - -There should be the view models, the structure that the client will be able to see. - -Example: - -```go -import ( - "github.com/kataras/iris/v12/_examples/mvc/login/datamodels" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" -) - -type User struct { - datamodels.User -} - -func (m User) IsValid() bool { - /* do some checks and return true if it's valid... */ - return m.ID > 0 -} -``` - -Iris is able to convert any custom data Structure into an HTTP Response Dispatcher, -so theoretically, something like the following is permitted if it's really necessary; - -```go -// Dispatch completes the `kataras/iris/mvc#Result` interface. -// Sends a `User` as a controlled http response. -// If its ID is zero or less then it returns a 404 not found error -// else it returns its json representation, -// (just like the controller's functions do for custom types by default). -// -// Don't overdo it, the application's logic should not be here. -// It's just one more step of validation before the response, -// simple checks can be added here. -// -// It's just a showcase, -// imagine the potentials this feature gives when designing a bigger application. -// -// This is called where the return value from a controller's method functions -// is type of `User`. -// For example the `controllers/user_controller.go#GetBy`. -func (m User) Dispatch(ctx iris.Context) { - if !m.IsValid() { - ctx.NotFound() - return - } - ctx.JSON(m, context.JSON{Indent: " "}) -} -``` - -However, we will use the "datamodels" as the only one models package because -User structure doesn't contain any sensitive data, clients are able to see all of its fields -and we don't need any extra functionality or validation inside it. \ No newline at end of file diff --git a/_examples/mvc/login/web/views/shared/error.html b/_examples/mvc/login/web/views/shared/error.html deleted file mode 100644 index 3847119db3..0000000000 --- a/_examples/mvc/login/web/views/shared/error.html +++ /dev/null @@ -1,15 +0,0 @@ -

Error.

-

An error occurred while processing your request.

- -

{{.Message}}

- - \ No newline at end of file diff --git a/_examples/mvc/login/web/views/shared/layout.html b/_examples/mvc/login/web/views/shared/layout.html deleted file mode 100644 index 75e14fb05c..0000000000 --- a/_examples/mvc/login/web/views/shared/layout.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - {{.Title}} - - - - - {{ yield }} - - - \ No newline at end of file diff --git a/_examples/mvc/login/web/views/user/login.html b/_examples/mvc/login/web/views/user/login.html deleted file mode 100644 index e84528a688..0000000000 --- a/_examples/mvc/login/web/views/user/login.html +++ /dev/null @@ -1,11 +0,0 @@ -
-
- - - - - - - -
-
\ No newline at end of file diff --git a/_examples/mvc/login/web/views/user/me.html b/_examples/mvc/login/web/views/user/me.html deleted file mode 100644 index d15da9306f..0000000000 --- a/_examples/mvc/login/web/views/user/me.html +++ /dev/null @@ -1,3 +0,0 @@ -

- Welcome back {{.User.Firstname}}! -

\ No newline at end of file diff --git a/_examples/mvc/login/web/views/user/register.html b/_examples/mvc/login/web/views/user/register.html deleted file mode 100644 index e46cc54d54..0000000000 --- a/_examples/mvc/login/web/views/user/register.html +++ /dev/null @@ -1,14 +0,0 @@ -
-
- - - - - - - - - - -
-
\ No newline at end of file diff --git a/_examples/mvc/middleware/main.go b/_examples/mvc/middleware/main.go deleted file mode 100644 index 6cd4e53c3f..0000000000 --- a/_examples/mvc/middleware/main.go +++ /dev/null @@ -1,47 +0,0 @@ -// Package main shows how you can add middleware to an mvc Application, simply -// by using its `Router` which is a sub router(an iris.Party) of the main iris app. -package main - -import ( - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/cache" - "github.com/kataras/iris/v12/mvc" -) - -var cacheHandler = cache.Handler(10 * time.Second) - -func main() { - app := iris.New() - app.Logger().SetLevel("debug") - - mvc.Configure(app, configure) - - // http://localhost:8080 - // http://localhost:8080/other - // - // refresh every 10 seconds and you'll see different time output. - app.Listen(":8080") -} - -func configure(m *mvc.Application) { - m.Router.Use(cacheHandler) - m.Handle(&exampleController{ - timeFormat: "Mon, Jan 02 2006 15:04:05", - }) -} - -type exampleController struct { - timeFormat string -} - -func (c *exampleController) Get() string { - now := time.Now().Format(c.timeFormat) - return "last time executed without cache: " + now -} - -func (c *exampleController) GetOther() string { - now := time.Now().Format(c.timeFormat) - return "/other: " + now -} diff --git a/_examples/mvc/middleware/per-method/main.go b/_examples/mvc/middleware/per-method/main.go deleted file mode 100644 index f4e135602a..0000000000 --- a/_examples/mvc/middleware/per-method/main.go +++ /dev/null @@ -1,105 +0,0 @@ -/* -If you want to use it as middleware for the entire controller -you can use its router which is just a sub router to add it as you normally do with standard API: - -I'll show you 4 different methods for adding a middleware into an mvc application, -all of those 4 do exactly the same thing, select what you prefer, -I prefer the last code-snippet when I need the middleware to be registered somewhere -else as well, otherwise I am going with the first one: - -```go -// 1 -mvc.Configure(app.Party("/user"), func(m *mvc.Application) { - m.Router.Use(cache.Handler(10*time.Second)) -}) -``` - -```go -// 2 -// same: -userRouter := app.Party("/user") -userRouter.Use(cache.Handler(10*time.Second)) -mvc.Configure(userRouter, ...) -``` - -```go -// 3 -// same: -userRouter := app.Party("/user", cache.Handler(10*time.Second)) -mvc.Configure(userRouter, ...) -``` - -```go -// 4 -// same: -app.PartyFunc("/user", func(r iris.Party){ - r.Use(cache.Handler(10*time.Second)) - mvc.Configure(r, ...) -}) -``` - -If you want to use a middleware for a single route, -for a single controller's method that is already registered by the engine -and not by custom `Handle` (which you can add -the middleware there on the last parameter) and it's not depend on the `Next Handler` to do its job -then you just call it on the method: - -```go -var myMiddleware := myMiddleware.New(...) // this should return an iris/context.Handler - -type UserController struct{} -func (c *UserController) GetSomething(ctx iris.Context) { - // ctx.Proceed checks if myMiddleware called `ctx.Next()` - // inside it and returns true if so, otherwise false. - nextCalled := ctx.Proceed(myMiddleware) - if !nextCalled { - return - } - - // else do the job here, it's allowed -} -``` - -And last, if you want to add a middleware on a specific method -and it depends on the next and the whole chain then you have to do it -using the `AfterActivation` like the example below: -*/ -package main - -import ( - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/cache" - "github.com/kataras/iris/v12/mvc" -) - -var cacheHandler = cache.Handler(10 * time.Second) - -func main() { - app := iris.New() - // You don't have to use .Configure if you do it all in the main func - // mvc.Configure and mvc.New(...).Configure() are just helpers to split - // your code better, here we use the simplest form: - m := mvc.New(app) - m.Handle(&exampleController{}) - - app.Listen(":8080") -} - -type exampleController struct{} - -func (c *exampleController) AfterActivation(a mvc.AfterActivation) { - // select the route based on the method name you want to - // modify. - index := a.GetRoute("Get") - // just prepend the handler(s) as middleware(s) you want to use. - // or append for "done" handlers. - index.Handlers = append([]iris.Handler{cacheHandler}, index.Handlers...) -} - -func (c *exampleController) Get() string { - // refresh every 10 seconds and you will see different time output. - now := time.Now().Format("Mon, Jan 02 2006 15:04:05") - return "last time executed without cache: " + now -} diff --git a/_examples/mvc/middleware/without-ctx-next/main.go b/_examples/mvc/middleware/without-ctx-next/main.go deleted file mode 100644 index 1473880e16..0000000000 --- a/_examples/mvc/middleware/without-ctx-next/main.go +++ /dev/null @@ -1,48 +0,0 @@ -/*Package main shows how to add done handlers in an MVC application without -the necessity of `ctx.Next()` inside the controller's methods. - -When we want the `Done` handlers of that specific mvc app's `Party` -to be executed but we don't want to add `ctx.Next()` on the `exampleController#EndRequest`*/ -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" -) - -func main() { - app := iris.New() - app.Get("/", func(ctx iris.Context) { ctx.Redirect("/example") }) - - exampleRouter := app.Party("/example") - { - exampleRouter.SetExecutionRules(iris.ExecutionRules{ - Done: iris.ExecutionOptions{Force: true}, - }) - - exampleRouter.Done(doneHandler) - - m := mvc.New(exampleRouter) - m.Handle(&exampleController{}) - } - - app.Listen(":8080") -} - -func doneHandler(ctx iris.Context) { - ctx.WriteString("\nFrom Done Handler") -} - -type exampleController struct{} - -func (c *exampleController) Get() string { - return "From Main Handler" - // Note that here we don't binding the `Context`, and we don't call its `Next()` - // function in order to call the `doneHandler`, - // this is done automatically for us because we changed the execution rules with the - // `SetExecutionRules`. - // - // Therefore the final output is: - // From Main Handler - // From Done Handler -} diff --git a/_examples/mvc/overview/Dockerfile b/_examples/mvc/overview/Dockerfile deleted file mode 100644 index 16c9113d46..0000000000 --- a/_examples/mvc/overview/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# docker build -t app . -# docker run --rm -it -p 8080:8080 app:latest -FROM golang:latest AS builder -RUN apt-get update -ENV GO111MODULE=on \ - CGO_ENABLED=0 \ - GOOS=linux \ - GOARCH=amd64 -WORKDIR /go/src/app -COPY go.mod . -RUN go mod download -COPY . . -RUN go install - -FROM scratch -COPY --from=builder /go/bin/app . -ENTRYPOINT ["./app"] \ No newline at end of file diff --git a/_examples/mvc/overview/README.md b/_examples/mvc/overview/README.md deleted file mode 100644 index e58e6ff7ca..0000000000 --- a/_examples/mvc/overview/README.md +++ /dev/null @@ -1,378 +0,0 @@ -# Quick start - -The following guide is just a simple example of usage of some of the **Iris MVC** features. You are not limited to that data structure or code flow. - -Create a folder, let's assume its path is `app`. The structure should look like that: - -``` -│ main.go -│ go.mod -│ go.sum -└───environment -│ environment.go -└───model -│ request.go -│ response.go -└───database -│ database.go -│ mysql.go -│ sqlite.go -└───service -│ greet_service.go -└───controller - greet_controller.go -``` - -Navigate to that `app` folder and execute the following command: - -```sh -$ go init app -$ go get -u github.com/kataras/iris/v12@master -# or @latest for the latest official release. -``` - -## Environment - -Let's start by defining the available environments that our web-application can behave on. - -We'll just work on two available environments, the "development" and the "production", as they define the two most common scenarios. The `ReadEnv` will read from the `Env` type of a system's environment variable (see `main.go` in the end of the page). - -Create a `environment/environment.go` file and put the following contents: - -```go -package environment - -import ( - "os" - "strings" -) - -const ( - PROD Env = "production" - DEV Env = "development" -) - -type Env string - -func (e Env) String() string { - return string(e) -} - -func ReadEnv(key string, def Env) Env { - v := Getenv(key, def.String()) - if v == "" { - return def - } - - env := Env(strings.ToLower(v)) - switch env { - case PROD, DEV: // allowed. - default: - panic("unexpected environment " + v) - } - - return env -} - -func Getenv(key string, def string) string { - if v := os.Getenv(key); v != "" { - return v - } - - return def -} -``` - -## Database - -We will use two database management systems, the `MySQL` and the `SQLite`. The first one for "production" use and the other for "development". - -Create a `database/database.go` file and copy-paste the following: - -```go -package database - -import "app/environment" - -type DB interface { - Exec(q string) error -} - -func NewDB(env environment.Env) DB { - switch env { - case environment.PROD: - return &mysql{} - case environment.DEV: - return &sqlite{} - default: - panic("unknown environment") - } -} -``` - -Let's simulate our MySQL and SQLite `DB` instances. Create a `database/mysql.go` file which looks like the following one: - -```go -package database - -import "fmt" - -type mysql struct{} - -func (db *mysql) Exec(q string) error { - return fmt.Errorf("mysql: not implemented <%s>", q) -} -``` - -And a `database/sqlite.go` file. - -```go -package database - -type sqlite struct{} - -func (db *sqlite) Exec(q string) error { return nil } -``` - -The `DB` depends on the `Environment. - -> A practical and operational database example, including Docker images, can be found at the following guide: https://github.com/kataras/iris/tree/master/_examples/database/mysql - -## Service - -We'll need a service that will communicate with a database instance in behalf of our Controller(s). - -In our case we will only need a single service, the Greet Service. - -For the sake of the example, let's use two implementations of a greet service based on the `Environment`. The `GreetService` interface contains a single method of `Say(input string) (output string, err error)`. Create a `./service/greet_service.go` file and write the following code: - -```go -package service - -import ( - "fmt" - - "app/database" - "app/environment" -) - -// GreetService example service. -type GreetService interface { - Say(input string) (string, error) -} - -// NewGreetService returns a service backed with a "db" based on "env". -func NewGreetService(env environment.Env, db database.DB) GreetService { - service := &greeter{db: db, prefix: "Hello"} - - switch env { - case environment.PROD: - return service - case environment.DEV: - return &greeterWithLogging{service} - default: - panic("unknown environment") - } -} - -type greeter struct { - prefix string - db database.DB -} - -func (s *greeter) Say(input string) (string, error) { - if err := s.db.Exec("simulate a query..."); err != nil { - return "", err - } - - result := s.prefix + " " + input - return result, nil -} - -type greeterWithLogging struct { - *greeter -} - -func (s *greeterWithLogging) Say(input string) (string, error) { - result, err := s.greeter.Say(input) - fmt.Printf("result: %s\nerror: %v\n", result, err) - return result, err -} - -``` - -The `greeter` will be used on "production" and the `greeterWithLogging` on "development". The `GreetService` depends on the `Environment` and the `DB`. - -## Models - -Continue by creating our HTTP request and response models. - -Create a `model/request.go` file and copy-paste the following code: - -```go -package model - -type Request struct { - Name string `url:"name"` -} -``` - -Same for the `model/response.go` file. - -```go -package model - -type Response struct { - Message string `json:"msg"` -} -``` - -The server will accept a URL Query Parameter of `name` (e.g. `/greet?name=kataras`) and will reply back with a JSON message. - -## Controller - -MVC Controllers are responsible for controlling the flow of the application execution. When you make a request (means request a page) to MVC Application, a controller is responsible for returning the response to that request. - -We will only need the `GreetController` for our mini web-application. Create a file at `controller/greet_controller.go` which looks like that: - -```go -package controller - -import ( - "app/model" - "app/service" -) - -type GreetController struct { - Service service.GreetService - // Ctx iris.Context -} - -func (c *GreetController) Get(req model.Request) (model.Response, error) { - message, err := c.Service.Say(req.Name) - if err != nil { - return model.Response{}, err - } - - return model.Response{Message: message}, nil -} -``` - -The `GreetController` depends on the `GreetService`. It serves the `GET: /greet` index path through its `Get` method. The `Get` method accepts a `model.Request` which contains a single field name of `Name` which will be extracted from the `URL Query Parameter 'name'` (because it's a `GET` requst and its `url:"name"` struct field). - -## Wrap up - -```sh - +-------------------+ - | Env (DEV, PROD) | - +---------+---------+ - | | | - | | | - | | | - DEV | | | PROD --------------------+---------------------+ | +----------------------+------------------- - | | | - | | | - +---+-----+ +----------------v------------------+ +----+----+ - | sqlite | | NewDB(Env) DB | | mysql | - +---+-----+ +----------------+---+--------------+ +----+----+ - | | | | - | | | | - | | | | - +--------------+-----+ +-------------------v---v-----------------+ +----+------+ - | greeterWithLogging | | NewGreetService(Env, DB) GreetService | | greeter | - +--------------+-----+ +---------------------------+-------------+ +----+------+ - | | | - | | | - | +-----------------------------------------+ | - | | GreetController | | | - | | | | | - | | - Service GreetService <-- | | - | | | | - | +-------------------+---------------------+ | - | | | - | | | - | | | - | +-----------+-----------+ | - | | HTTP Request | | - | +-----------------------+ | - | | /greet?name=kataras | | - | +-----------+-----------+ | - | | | -+------------------+--------+ +------------+------------+ +-------+------------------+ -| model.Response (JSON) | | Response (JSON, error) | | Bad Request | -+---------------------------+ +-------------------------+ +--------------------------+ -| { | | mysql: not implemented | -| "msg": "Hello kataras" | +--------------------------+ -| } | -+---------------------------+ -``` - -Now it's the time to wrap all the above into our `main.go` file. Copy-paste the following code: - -```go -package main - -import ( - "app/controller" - "app/database" - "app/environment" - "app/service" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" -) - -func main() { - app := iris.New() - app.Get("/ping", pong).Describe("healthcheck") - - mvc.Configure(app.Party("/greet"), setup) - - // http://localhost:8080/greet?name=kataras - app.Listen(":8080", iris.WithLogLevel("debug")) -} - -func pong(ctx iris.Context) { - ctx.WriteString("pong") -} - -func setup(app *mvc.Application) { - // Register Dependencies. - app.Register( - environment.DEV, // DEV, PROD - database.NewDB, // sqlite, mysql - service.NewGreetService, // greeterWithLogging, greeter - ) - - // Register Controllers. - app.Handle(new(controller.GreetController)) -} -``` - -The `mvc.Application.Register` method registers one more dependencies, dependencies can depend on previously registered dependencies too. Thats the reason we pass, first, the `Environment(DEV)`, then the `NewDB` which depends on that `Environment`, following by the `NewGreetService` function which depends on both the `Environment(DEV)` and the `DB`. - -The `mvc.Application.Handle` registers a new controller, which depends on the `GreetService`, for the targeted sub-router of `Party("/greet")`. - -## Run - -Install [Go](https://golang.org/dl) and run the application with: - -```sh -go run main.go -``` - -
Docker - -Download the [Dockerfile](https://raw.githubusercontent.com/kataras/iris/9b93c0dbb491dcedf49c91e89ca13bab884d116f/_examples/mvc/overview/Dockerfile) and [docker-compose.yml](https://raw.githubusercontent.com/kataras/iris/9b93c0dbb491dcedf49c91e89ca13bab884d116f/_examples/mvc/overview/docker-compose.yml) files to the `app` folder. - -Install [Docker](https://www.docker.com/) and execute the following command: - -```sh -$ docker-compose up -``` -
- -Visit http://localhost:8080?name=kataras. - -Optionally, replace the `main.go`'s `app.Register(environment.DEV` with `environment.PROD`, restart the application and refresh. You will see that a new database (`sqlite`) and another service of (`greeterWithLogging`) will be binded to the `GreetController`. With **a single change** you achieve to completety change the result. diff --git a/_examples/mvc/overview/controller/greet_controller.go b/_examples/mvc/overview/controller/greet_controller.go deleted file mode 100644 index 737bd53722..0000000000 --- a/_examples/mvc/overview/controller/greet_controller.go +++ /dev/null @@ -1,23 +0,0 @@ -package controller - -import ( - "app/model" - "app/service" -) - -// GreetController handles the index. -type GreetController struct { - Service service.GreetService - // Ctx iris.Context -} - -// Get serves [GET] /. -// Query: name -func (c *GreetController) Get(req model.Request) (model.Response, error) { - message, err := c.Service.Say(req.Name) - if err != nil { - return model.Response{}, err - } - - return model.Response{Message: message}, nil -} diff --git a/_examples/mvc/overview/database/database.go b/_examples/mvc/overview/database/database.go deleted file mode 100644 index c99e96c66f..0000000000 --- a/_examples/mvc/overview/database/database.go +++ /dev/null @@ -1,20 +0,0 @@ -package database - -import "app/environment" - -// DB example database interface. -type DB interface { - Exec(q string) error -} - -// NewDB returns a database based on "env". -func NewDB(env environment.Env) DB { - switch env { - case environment.PROD: - return &mysql{} - case environment.DEV: - return &sqlite{} - default: - panic("unknown environment") - } -} diff --git a/_examples/mvc/overview/database/mysql.go b/_examples/mvc/overview/database/mysql.go deleted file mode 100644 index 5d14da05b3..0000000000 --- a/_examples/mvc/overview/database/mysql.go +++ /dev/null @@ -1,10 +0,0 @@ -package database - -import "fmt" - -type mysql struct{} - -func (db *mysql) Exec(q string) error { - // simulate an error response. - return fmt.Errorf("mysql: not implemented <%s>", q) -} diff --git a/_examples/mvc/overview/database/sqlite.go b/_examples/mvc/overview/database/sqlite.go deleted file mode 100644 index 956a82fd7d..0000000000 --- a/_examples/mvc/overview/database/sqlite.go +++ /dev/null @@ -1,5 +0,0 @@ -package database - -type sqlite struct{} - -func (db *sqlite) Exec(q string) error { return nil } diff --git a/_examples/mvc/overview/docker-compose.yml b/_examples/mvc/overview/docker-compose.yml deleted file mode 100644 index db9065062e..0000000000 --- a/_examples/mvc/overview/docker-compose.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: '3.1' - -services: - app: - build: . - ports: - - 8080:8080 - environment: - PORT: 8080 - ENVIRONMENT: development - restart: on-failure - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8080/ping"] - interval: 30s - timeout: 10s - retries: 5 diff --git a/_examples/mvc/overview/environment/environment.go b/_examples/mvc/overview/environment/environment.go deleted file mode 100644 index 519a7cc91e..0000000000 --- a/_examples/mvc/overview/environment/environment.go +++ /dev/null @@ -1,50 +0,0 @@ -package environment - -import ( - "os" - "strings" -) - -// Available environments example. -const ( - PROD Env = "production" - DEV Env = "development" -) - -// Env is the environment type. -type Env string - -// String just returns the string representation of the Env. -func (e Env) String() string { - return string(e) -} - -// ReadEnv returns the environment of the system environment variable of "key". -// Returns the "def" if not found. -// Reports a panic message if the environment variable found -// but the Env is unknown. -func ReadEnv(key string, def Env) Env { - v := Getenv(key, def.String()) - if v == "" { - return def - } - - env := Env(strings.ToLower(v)) - switch env { - case PROD, DEV: // allowed. - default: - panic("unexpected environment " + v) - } - - return env -} - -// Getenv returns the value of a system environment variable "key". -// Defaults to "def" if not found. -func Getenv(key string, def string) string { - if v := os.Getenv(key); v != "" { - return v - } - - return def -} diff --git a/_examples/mvc/overview/go.mod b/_examples/mvc/overview/go.mod deleted file mode 100644 index cb0cb9cd15..0000000000 --- a/_examples/mvc/overview/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module app - -go 1.15 - -require github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd diff --git a/_examples/mvc/overview/main.go b/_examples/mvc/overview/main.go deleted file mode 100644 index 7476d9b7e9..0000000000 --- a/_examples/mvc/overview/main.go +++ /dev/null @@ -1,85 +0,0 @@ -package main - -import ( - "app/controller" - "app/database" - "app/environment" - "app/service" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" -) - -func main() { - app := iris.New() - app.Get("/ping", pong).Describe("healthcheck") - - mvc.Configure(app.Party("/greet"), setup) - - // http://localhost:8080/greet?name=kataras - addr := ":" + environment.Getenv("PORT", "8080") - app.Listen(addr, iris.WithLogLevel("debug")) -} - -func pong(ctx iris.Context) { - ctx.WriteString("pong") -} - -/* - +-------------------+ - | Env (DEV, PROD) | - +---------+---------+ - | | | - | | | - | | | - DEV | | | PROD --------------------+---------------------+ | +----------------------+------------------- - | | | - | | | - +---+-----+ +----------------v------------------+ +----+----+ - | sqlite | | NewDB(Env) DB | | mysql | - +---+-----+ +----------------+---+--------------+ +----+----+ - | | | | - | | | | - | | | | - +--------------+-----+ +-------------------v---v-----------------+ +----+------+ - | greeterWithLogging | | NewGreetService(Env, DB) GreetService | | greeter | - +--------------+-----+ +---------------------------+-------------+ +----+------+ - | | | - | | | - | +-----------------------------------------+ | - | | GreetController | | | - | | | | | - | | - Service GreetService <-- | | - | | | | - | +-------------------+---------------------+ | - | | | - | | | - | | | - | +-----------+-----------+ | - | | HTTP Request | | - | +-----------------------+ | - | | /greet?name=kataras | | - | +-----------+-----------+ | - | | | -+------------------+--------+ +------------+------------+ +-------+------------------+ -| model.Response (JSON) | | Response (JSON, error) | | Bad Request | -+---------------------------+ +-------------------------+ +--------------------------+ -| { | | mysql: not implemented | -| "msg": "Hello kataras" | +--------------------------+ -| } | -+---------------------------+ -*/ -func setup(app *mvc.Application) { - // Register Dependencies. - // Tip: A dependency can depend on other dependencies too. - env := environment.ReadEnv("ENVIRONMENT", environment.DEV) - app.Register( - env, // DEV, PROD - database.NewDB, // sqlite, mysql - service.NewGreetService, // greeterWithLogging, greeter - ) - - // Register Controllers. - app.Handle(new(controller.GreetController)) -} diff --git a/_examples/mvc/overview/model/request.go b/_examples/mvc/overview/model/request.go deleted file mode 100644 index 03a826ed4b..0000000000 --- a/_examples/mvc/overview/model/request.go +++ /dev/null @@ -1,6 +0,0 @@ -package model - -// Request example incoming request. -type Request struct { - Name string `json:"name" url:"name"` -} diff --git a/_examples/mvc/overview/model/response.go b/_examples/mvc/overview/model/response.go deleted file mode 100644 index 18e7855fea..0000000000 --- a/_examples/mvc/overview/model/response.go +++ /dev/null @@ -1,6 +0,0 @@ -package model - -// Response example server's response. -type Response struct { - Message string `json:"msg"` -} diff --git a/_examples/mvc/overview/service/greet_service.go b/_examples/mvc/overview/service/greet_service.go deleted file mode 100644 index c0d493d85e..0000000000 --- a/_examples/mvc/overview/service/greet_service.go +++ /dev/null @@ -1,51 +0,0 @@ -package service - -import ( - "fmt" - - "app/database" - "app/environment" -) - -// GreetService example service. -type GreetService interface { - Say(input string) (string, error) -} - -// NewGreetService returns a service backed with a "db" based on "env". -func NewGreetService(env environment.Env, db database.DB) GreetService { - service := &greeter{db: db, prefix: "Hello"} - - switch env { - case environment.PROD: - return service - case environment.DEV: - return &greeterWithLogging{service} - default: - panic("unknown environment") - } -} - -type greeter struct { - prefix string - db database.DB -} - -func (s *greeter) Say(input string) (string, error) { - if err := s.db.Exec("simulate a query..."); err != nil { - return "", err - } - - result := s.prefix + " " + input - return result, nil -} - -type greeterWithLogging struct { - *greeter -} - -func (s *greeterWithLogging) Say(input string) (string, error) { - result, err := s.greeter.Say(input) - fmt.Printf("result: %s\nerror: %v\n", result, err) - return result, err -} diff --git a/_examples/mvc/regexp/main.go b/_examples/mvc/regexp/main.go deleted file mode 100644 index a68fdf2610..0000000000 --- a/_examples/mvc/regexp/main.go +++ /dev/null @@ -1,49 +0,0 @@ -// Package main shows how to match "/xxx.json" in MVC handler. -package main - -/* -There is no MVC naming pattern for such these things,you can imagine the limitations of that. -Instead you can use the `BeforeActivation` on your controller to add more advanced routing features -(https://github.com/kataras/iris/tree/master/_examples/routing). - -You can also create your own macro, -i.e: /{file:json} or macro function of a specific parameter type i.e: (/{file:string json()}). -Read the routing examples and you will gain a deeper view, there are all covered. -*/ - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" -) - -func main() { - app := iris.New() - - mvcApp := mvc.New(app.Party("/module")) - mvcApp.Handle(new(myController)) - - // http://localhost:8080/module/xxx.json (OK) - // http://localhost:8080/module/xxx.xml (Not Found) - app.Listen(":8080") -} - -type myController struct{} - -func (m *myController) BeforeActivation(b mvc.BeforeActivation) { - // b.Dependencies().Register - // b.Router().Use/UseGlobal/Done // and any standard API call you already know - - // 1-> Method - // 2-> Path - // 3-> The controller's function name to be parsed as handler - // 4-> Any handlers that should run before the HandleJSON - - // "^[a-zA-Z0-9_.-]+.json$)" to validate file-name pattern and json - // or just: ".json$" to validate suffix. - - b.Handle("GET", "/{file:string regexp(^[a-zA-Z0-9_.-]+.json$))}", "HandleJSON" /*optionalMiddleware*/) -} - -func (m *myController) HandleJSON(file string) string { - return "custom serving of json: " + file -} diff --git a/_examples/mvc/repository/datamodels/README.md b/_examples/mvc/repository/datamodels/README.md deleted file mode 100644 index 75a1b75c14..0000000000 --- a/_examples/mvc/repository/datamodels/README.md +++ /dev/null @@ -1 +0,0 @@ -# Data Model Layer \ No newline at end of file diff --git a/_examples/mvc/repository/datamodels/movie.go b/_examples/mvc/repository/datamodels/movie.go deleted file mode 100644 index 4499ad47f2..0000000000 --- a/_examples/mvc/repository/datamodels/movie.go +++ /dev/null @@ -1,18 +0,0 @@ -// file: datamodels/movie.go - -package datamodels - -// Movie is our sample data structure. -// Keep note that the tags for public-use (for our web app) -// should be kept in other file like "web/viewmodels/movie.go" -// which could wrap by embedding the datamodels.Movie or -// declare new fields instead butwe will use this datamodel -// as the only one Movie model in our application, -// for the sake of simplicty. -type Movie struct { - ID int64 `json:"id"` - Name string `json:"name"` - Year int `json:"year"` - Genre string `json:"genre"` - Poster string `json:"poster"` -} diff --git a/_examples/mvc/repository/datasource/README.md b/_examples/mvc/repository/datasource/README.md deleted file mode 100644 index b5633a545e..0000000000 --- a/_examples/mvc/repository/datasource/README.md +++ /dev/null @@ -1 +0,0 @@ -# Data Source / Data Store Layer \ No newline at end of file diff --git a/_examples/mvc/repository/datasource/movies.go b/_examples/mvc/repository/datasource/movies.go deleted file mode 100644 index 518245df1a..0000000000 --- a/_examples/mvc/repository/datasource/movies.go +++ /dev/null @@ -1,44 +0,0 @@ -// file: datasource/movies.go - -package datasource - -import "github.com/kataras/iris/v12/_examples/mvc/repository/datamodels" - -// Movies is our imaginary data source. -var Movies = map[int64]datamodels.Movie{ - 1: { - ID: 1, - Name: "Casablanca", - Year: 1942, - Genre: "Romance", - Poster: "https://iris-go.com/images/examples/mvc-movies/1.jpg", - }, - 2: { - ID: 2, - Name: "Gone with the Wind", - Year: 1939, - Genre: "Romance", - Poster: "https://iris-go.com/images/examples/mvc-movies/2.jpg", - }, - 3: { - ID: 3, - Name: "Citizen Kane", - Year: 1941, - Genre: "Mystery", - Poster: "https://iris-go.com/images/examples/mvc-movies/3.jpg", - }, - 4: { - ID: 4, - Name: "The Wizard of Oz", - Year: 1939, - Genre: "Fantasy", - Poster: "https://iris-go.com/images/examples/mvc-movies/4.jpg", - }, - 5: { - ID: 5, - Name: "North by Northwest", - Year: 1959, - Genre: "Thriller", - Poster: "https://iris-go.com/images/examples/mvc-movies/5.jpg", - }, -} diff --git a/_examples/mvc/repository/folder_structure.png b/_examples/mvc/repository/folder_structure.png deleted file mode 100644 index 0029e8f74c..0000000000 Binary files a/_examples/mvc/repository/folder_structure.png and /dev/null differ diff --git a/_examples/mvc/repository/main.go b/_examples/mvc/repository/main.go deleted file mode 100644 index 1cd430b712..0000000000 --- a/_examples/mvc/repository/main.go +++ /dev/null @@ -1,53 +0,0 @@ -// file: main.go - -package main - -import ( - "github.com/kataras/iris/v12/_examples/mvc/repository/datasource" - "github.com/kataras/iris/v12/_examples/mvc/repository/repositories" - "github.com/kataras/iris/v12/_examples/mvc/repository/services" - "github.com/kataras/iris/v12/_examples/mvc/repository/web/controllers" - "github.com/kataras/iris/v12/_examples/mvc/repository/web/middleware" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" -) - -func main() { - app := iris.New() - app.Logger().SetLevel("debug") - - // Load the template files. - app.RegisterView(iris.HTML("./web/views", ".html")) - - // Serve our controllers. - mvc.New(app.Party("/hello")).Handle(new(controllers.HelloController)) - // You can also split the code you write to configure an mvc.Application - // using the `mvc.Configure` method, as shown below. - mvc.Configure(app.Party("/movies"), movies) - - // http://localhost:8080/hello - // http://localhost:8080/hello/iris - // http://localhost:8080/movies - // http://localhost:8080/movies/1 - app.Listen(":8080", iris.WithOptimizations) -} - -// note the mvc.Application, it's not iris.Application. -func movies(app *mvc.Application) { - // Add the basic authentication(admin:password) middleware - // for the /movies based requests. - app.Router.Use(middleware.BasicAuth) - - // Create our movie repository with some (memory) data from the datasource. - repo := repositories.NewMovieRepository(datasource.Movies) - // Create our movie service, we will bind it to the movie app's dependencies. - movieService := services.NewMovieService(repo) - app.Register(movieService) - - // serve our movies controller. - // Note that you can serve more than one controller - // you can also create child mvc apps using the `movies.Party(relativePath)` or `movies.Clone(app.Party(...))` - // if you want. - app.Handle(new(controllers.MovieController)) -} diff --git a/_examples/mvc/repository/models/README.md b/_examples/mvc/repository/models/README.md deleted file mode 100644 index 280664ee62..0000000000 --- a/_examples/mvc/repository/models/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Domain Models - -There should be the domain/business-level models. - -Example: - -```go -import "github.com/kataras/iris/v12/_examples/mvc/repository/datamodels" - -type Movie struct { - datamodels.Movie -} - -func (m Movie) Validate() (Movie, error) { - /* do some checks and return an error if that Movie is not valid */ -} -``` - -However, we will use the "datamodels" as the only one models package because -Movie structure we don't need any extra functionality or validation inside it. \ No newline at end of file diff --git a/_examples/mvc/repository/repositories/README.md b/_examples/mvc/repository/repositories/README.md deleted file mode 100644 index 6de39915c1..0000000000 --- a/_examples/mvc/repository/repositories/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Repositories - -The package which has direct access to the "datasource" and can manipulate data directly. \ No newline at end of file diff --git a/_examples/mvc/repository/repositories/movie_repository.go b/_examples/mvc/repository/repositories/movie_repository.go deleted file mode 100644 index 6a79aeee44..0000000000 --- a/_examples/mvc/repository/repositories/movie_repository.go +++ /dev/null @@ -1,176 +0,0 @@ -// file: repositories/movie_repository.go - -package repositories - -import ( - "errors" - "sync" - - "github.com/kataras/iris/v12/_examples/mvc/repository/datamodels" -) - -// Query represents the visitor and action queries. -type Query func(datamodels.Movie) bool - -// MovieRepository handles the basic operations of a movie entity/model. -// It's an interface in order to be testable, i.e a memory movie repository or -// a connected to an sql database. -type MovieRepository interface { - Exec(query Query, action Query, limit int, mode int) (ok bool) - - Select(query Query) (movie datamodels.Movie, found bool) - SelectMany(query Query, limit int) (results []datamodels.Movie) - - InsertOrUpdate(movie datamodels.Movie) (updatedMovie datamodels.Movie, err error) - Delete(query Query, limit int) (deleted bool) -} - -// NewMovieRepository returns a new movie memory-based repository, -// the one and only repository type in our example. -func NewMovieRepository(source map[int64]datamodels.Movie) MovieRepository { - return &movieMemoryRepository{source: source} -} - -// movieMemoryRepository is a "MovieRepository" -// which manages the movies using the memory data source (map). -type movieMemoryRepository struct { - source map[int64]datamodels.Movie - mu sync.RWMutex -} - -const ( - // ReadOnlyMode will RLock(read) the data . - ReadOnlyMode = iota - // ReadWriteMode will Lock(read/write) the data. - ReadWriteMode -) - -func (r *movieMemoryRepository) Exec(query Query, action Query, actionLimit int, mode int) (ok bool) { - loops := 0 - - if mode == ReadOnlyMode { - r.mu.RLock() - defer r.mu.RUnlock() - } else { - r.mu.Lock() - defer r.mu.Unlock() - } - - for _, movie := range r.source { - ok = query(movie) - if ok { - if action(movie) { - loops++ - if actionLimit >= loops { - break // break - } - } - } - } - - return -} - -// Select receives a query function -// which is fired for every single movie model inside -// our imaginary data source. -// When that function returns true then it stops the iteration. -// -// It returns the query's return last known "found" value -// and the last known movie model -// to help callers to reduce the LOC. -// -// It's actually a simple but very clever prototype function -// I'm using everywhere since I firstly think of it, -// hope you'll find it very useful as well. -func (r *movieMemoryRepository) Select(query Query) (movie datamodels.Movie, found bool) { - found = r.Exec(query, func(m datamodels.Movie) bool { - movie = m - return true - }, 1, ReadOnlyMode) - - // set an empty datamodels.Movie if not found at all. - if !found { - movie = datamodels.Movie{} - } - - return -} - -// SelectMany same as Select but returns one or more datamodels.Movie as a slice. -// If limit <=0 then it returns everything. -func (r *movieMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.Movie) { - r.Exec(query, func(m datamodels.Movie) bool { - results = append(results, m) - return true - }, limit, ReadOnlyMode) - - return -} - -// InsertOrUpdate adds or updates a movie to the (memory) storage. -// -// Returns the new movie and an error if any. -func (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamodels.Movie, error) { - id := movie.ID - - if id == 0 { // Create new action - var lastID int64 - // find the biggest ID in order to not have duplications - // in productions apps you can use a third-party - // library to generate a UUID as string. - r.mu.RLock() - for _, item := range r.source { - if item.ID > lastID { - lastID = item.ID - } - } - r.mu.RUnlock() - - id = lastID + 1 - movie.ID = id - - // map-specific thing - r.mu.Lock() - r.source[id] = movie - r.mu.Unlock() - - return movie, nil - } - - // Update action based on the movie.ID, - // here we will allow updating the poster and genre if not empty. - // Alternatively we could do pure replace instead: - // r.source[id] = movie - // and comment the code below; - current, exists := r.Select(func(m datamodels.Movie) bool { - return m.ID == id - }) - - if !exists { // ID is not a real one, return an error. - return datamodels.Movie{}, errors.New("failed to update a nonexistent movie") - } - - // or comment these and r.source[id] = m for pure replace - if movie.Poster != "" { - current.Poster = movie.Poster - } - - if movie.Genre != "" { - current.Genre = movie.Genre - } - - // map-specific thing - r.mu.Lock() - r.source[id] = current - r.mu.Unlock() - - return movie, nil -} - -func (r *movieMemoryRepository) Delete(query Query, limit int) bool { - return r.Exec(query, func(m datamodels.Movie) bool { - delete(r.source, m.ID) - return true - }, limit, ReadWriteMode) -} diff --git a/_examples/mvc/repository/services/README.md b/_examples/mvc/repository/services/README.md deleted file mode 100644 index 520875c94a..0000000000 --- a/_examples/mvc/repository/services/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Service Layer - -The package which has access to call functions from the "repositories" and "models" ("datamodels" only in that simple example). It should contain the domain logic. \ No newline at end of file diff --git a/_examples/mvc/repository/services/movie_service.go b/_examples/mvc/repository/services/movie_service.go deleted file mode 100644 index 5e1b028040..0000000000 --- a/_examples/mvc/repository/services/movie_service.go +++ /dev/null @@ -1,65 +0,0 @@ -// file: services/movie_service.go - -package services - -import ( - "github.com/kataras/iris/v12/_examples/mvc/repository/datamodels" - "github.com/kataras/iris/v12/_examples/mvc/repository/repositories" -) - -// MovieService handles some of the CRUID operations of the movie datamodel. -// It depends on a movie repository for its actions. -// It's here to decouple the data source from the higher level compoments. -// As a result a different repository type can be used with the same logic without any aditional changes. -// It's an interface and it's used as interface everywhere -// because we may need to change or try an experimental different domain logic at the future. -type MovieService interface { - GetAll() []datamodels.Movie - GetByID(id int64) (datamodels.Movie, bool) - DeleteByID(id int64) bool - UpdatePosterAndGenreByID(id int64, poster string, genre string) (datamodels.Movie, error) -} - -// NewMovieService returns the default movie service. -func NewMovieService(repo repositories.MovieRepository) MovieService { - return &movieService{ - repo: repo, - } -} - -type movieService struct { - repo repositories.MovieRepository -} - -// GetAll returns all movies. -func (s *movieService) GetAll() []datamodels.Movie { - return s.repo.SelectMany(func(_ datamodels.Movie) bool { - return true - }, -1) -} - -// GetByID returns a movie based on its id. -func (s *movieService) GetByID(id int64) (datamodels.Movie, bool) { - return s.repo.Select(func(m datamodels.Movie) bool { - return m.ID == id - }) -} - -// UpdatePosterAndGenreByID updates a movie's poster and genre. -func (s *movieService) UpdatePosterAndGenreByID(id int64, poster string, genre string) (datamodels.Movie, error) { - // update the movie and return it. - return s.repo.InsertOrUpdate(datamodels.Movie{ - ID: id, - Poster: poster, - Genre: genre, - }) -} - -// DeleteByID deletes a movie by its id. -// -// Returns true if deleted otherwise false. -func (s *movieService) DeleteByID(id int64) bool { - return s.repo.Delete(func(m datamodels.Movie) bool { - return m.ID == id - }, 1) -} diff --git a/_examples/mvc/repository/web/controllers/hello_controller.go b/_examples/mvc/repository/web/controllers/hello_controller.go deleted file mode 100644 index 21bbc99e48..0000000000 --- a/_examples/mvc/repository/web/controllers/hello_controller.go +++ /dev/null @@ -1,58 +0,0 @@ -// file: web/controllers/hello_controller.go - -package controllers - -import ( - "errors" - - "github.com/kataras/iris/v12/mvc" -) - -// HelloController is our sample controller -// it handles GET: /hello and GET: /hello/{name} -type HelloController struct{} - -var helloView = mvc.View{ - Name: "hello/index.html", - Data: map[string]interface{}{ - "Title": "Hello Page", - "MyMessage": "Welcome to my awesome website", - }, -} - -// Get will return a predefined view with bind data. -// -// `mvc.Result` is just an interface with a `Dispatch` function. -// `mvc.Response` and `mvc.View` are the builtin result type dispatchers -// you can even create custom response dispatchers by -// implementing the `github.com/kataras/iris/hero#Result` interface. -func (c *HelloController) Get() mvc.Result { - return helloView -} - -// you can define a standard error in order to re-use anywhere in your app. -var errBadName = errors.New("bad name") - -// you can just return it as error or even better -// wrap this error with an mvc.Response to make it an mvc.Result compatible type. -var badName = mvc.Response{Err: errBadName, Code: 400} - -// GetBy returns a "Hello {name}" response. -// Demos: -// curl -i http://localhost:8080/hello/iris -// curl -i http://localhost:8080/hello/anything -func (c *HelloController) GetBy(name string) mvc.Result { - if name != "iris" { - return badName - // or - // GetBy(name string) (mvc.Result, error) { - // return nil, errBadName - // } - } - - // return mvc.Response{Text: "Hello " + name} OR: - return mvc.View{ - Name: "hello/name.html", - Data: name, - } -} diff --git a/_examples/mvc/repository/web/controllers/movie_controller.go b/_examples/mvc/repository/web/controllers/movie_controller.go deleted file mode 100644 index a800d95983..0000000000 --- a/_examples/mvc/repository/web/controllers/movie_controller.go +++ /dev/null @@ -1,77 +0,0 @@ -// file: web/controllers/movie_controller.go - -package controllers - -import ( - "errors" - - "github.com/kataras/iris/v12/_examples/mvc/repository/datamodels" - "github.com/kataras/iris/v12/_examples/mvc/repository/services" - - "github.com/kataras/iris/v12" -) - -// MovieController is our /movies controller. -type MovieController struct { - // Our MovieService, it's an interface which - // is binded from the main application. - Service services.MovieService -} - -// Get returns list of the movies. -// Demo: -// curl -i http://localhost:8080/movies -// -// The correct way if you have sensitive data: -// func (c *MovieController) Get() (results []viewmodels.Movie) { -// data := c.Service.GetAll() -// -// for _, movie := range data { -// results = append(results, viewmodels.Movie{movie}) -// } -// return -// } -// otherwise just return the datamodels. -func (c *MovieController) Get() (results []datamodels.Movie) { - return c.Service.GetAll() -} - -// GetBy returns a movie. -// Demo: -// curl -i http://localhost:8080/movies/1 -func (c *MovieController) GetBy(id int64) (movie datamodels.Movie, found bool) { - return c.Service.GetByID(id) // it will throw 404 if not found. -} - -// PutBy updates a movie. -// Demo: -// curl -i -X PUT -F "genre=Thriller" -F "poster=@/Users/kataras/Downloads/out.gif" http://localhost:8080/movies/1 -func (c *MovieController) PutBy(ctx iris.Context, id int64) (datamodels.Movie, error) { - // get the request data for poster and genre - file, info, err := ctx.FormFile("poster") - if err != nil { - return datamodels.Movie{}, errors.New("failed due form file 'poster' missing") - } - // we don't need the file so close it now. - file.Close() - - // imagine that is the url of the uploaded file... - poster := info.Filename - genre := ctx.FormValue("genre") - - return c.Service.UpdatePosterAndGenreByID(id, poster, genre) -} - -// DeleteBy deletes a movie. -// Demo: -// curl -i -X DELETE -u admin:password http://localhost:8080/movies/1 -func (c *MovieController) DeleteBy(id int64) interface{} { - wasDel := c.Service.DeleteByID(id) - if wasDel { - // return the deleted movie's ID - return iris.Map{"deleted": id} - } - // right here we can see that a method function can return any of those two types(map or int), - // we don't have to specify the return type to a specific type. - return iris.StatusBadRequest -} diff --git a/_examples/mvc/repository/web/middleware/basicauth.go b/_examples/mvc/repository/web/middleware/basicauth.go deleted file mode 100644 index 5d61fc727e..0000000000 --- a/_examples/mvc/repository/web/middleware/basicauth.go +++ /dev/null @@ -1,12 +0,0 @@ -// file: web/middleware/basicauth.go - -package middleware - -import "github.com/kataras/iris/v12/middleware/basicauth" - -// BasicAuth middleware sample. -var BasicAuth = basicauth.New(basicauth.Config{ - Users: map[string]string{ - "admin": "password", - }, -}) diff --git a/_examples/mvc/repository/web/viewmodels/README.md b/_examples/mvc/repository/web/viewmodels/README.md deleted file mode 100644 index 1052028740..0000000000 --- a/_examples/mvc/repository/web/viewmodels/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# View Models - -There should be the view models, the structure that the client will be able to see. - -Example: - -```go -import ( - "github.com/kataras/iris/v12/_examples/mvc/repository/datamodels" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" -) - -type Movie struct { - datamodels.Movie -} - -func (m Movie) IsValid() bool { - /* do some checks and return true if it's valid... */ - return m.ID > 0 -} -``` - -Iris is able to convert any custom data Structure into an HTTP Response Dispatcher, -so theoretically, something like the following is permitted if it's really necessary; - -```go -// Dispatch completes the `kataras/iris/mvc#Result` interface. -// Sends a `Movie` as a controlled http response. -// If its ID is zero or less then it returns a 404 not found error -// else it returns its json representation, -// (just like the controller's functions do for custom types by default). -// -// Don't overdo it, the application's logic should not be here. -// It's just one more step of validation before the response, -// simple checks can be added here. -// -// It's just a showcase, -// imagine the potentials this feature gives when designing a bigger application. -// -// This is called where the return value from a controller's method functions -// is type of `Movie`. -// For example the `controllers/movie_controller.go#GetBy`. -func (m Movie) Dispatch(ctx iris.Context) { - if !m.IsValid() { - ctx.NotFound() - return - } - ctx.JSON(m, context.JSON{Indent: " "}) -} -``` - -However, we will use the "datamodels" as the only one models package because -Movie structure doesn't contain any sensitive data, clients are able to see all of its fields -and we don't need any extra functionality or validation inside it. \ No newline at end of file diff --git a/_examples/mvc/repository/web/views/hello/index.html b/_examples/mvc/repository/web/views/hello/index.html deleted file mode 100644 index 9e7b03d6d0..0000000000 --- a/_examples/mvc/repository/web/views/hello/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - {{.Title}} - My App - - - -

{{.MyMessage}}

- - - \ No newline at end of file diff --git a/_examples/mvc/repository/web/views/hello/name.html b/_examples/mvc/repository/web/views/hello/name.html deleted file mode 100644 index d6dd5ac66a..0000000000 --- a/_examples/mvc/repository/web/views/hello/name.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - {{.}}' Portfolio - My App - - - -

Hello {{.}}

- - - \ No newline at end of file diff --git a/_examples/mvc/session-controller/main.go b/_examples/mvc/session-controller/main.go deleted file mode 100644 index 953f6e6982..0000000000 --- a/_examples/mvc/session-controller/main.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - "fmt" - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" - "github.com/kataras/iris/v12/sessions" -) - -// VisitController handles the root route. -type VisitController struct { - // the current request session, automatically binded. - Session *sessions.Session - - // A time.time which is binded from the MVC application manually. - StartTime time.Time -} - -// Get handles index -// Method: GET -// Path: http://localhost:8080 -func (c *VisitController) Get() string { - // it increments a "visits" value of integer by one, - // if the entry with key 'visits' doesn't exist it will create it for you. - visits := c.Session.Increment("visits", 1) - // write the current, updated visits. - since := time.Now().Sub(c.StartTime).Seconds() - return fmt.Sprintf("%d visit(s) from my current session in %0.1f seconds of server's up-time", - visits, since) -} - -func newApp() *iris.Application { - app := iris.New() - // Configure sessions manager as we used to. - sess := sessions.New(sessions.Config{Cookie: "mysession_cookie_name"}) - app.Use(sess.Handler()) - - visitApp := mvc.New(app) - visitApp.Register(time.Now()) - // The `VisitController.Session` is automatically binded to the current `sessions.Session`. - - visitApp.Handle(new(VisitController)) - - return app -} - -func main() { - app := newApp() - - // 1. Prepare a client, e.g. your browser - // 2. navigate to http://localhost:8080 - // 3. refresh the page some times - // 4. close the browser - // 5. re-open the browser (if it wasn't in private mode) and re-play 2. - app.Listen(":8080") -} diff --git a/_examples/mvc/session-controller/main_test.go b/_examples/mvc/session-controller/main_test.go deleted file mode 100644 index e7ec59291c..0000000000 --- a/_examples/mvc/session-controller/main_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestMVCSession(t *testing.T) { - e := httptest.New(t, newApp(), httptest.URL("http://example.com")) - - e1 := e.GET("/").Expect().Status(httptest.StatusOK) - e1.Cookies().NotEmpty() - e1.Body().Contains("1 visit") - - e.GET("/").Expect().Status(httptest.StatusOK). - Body().Contains("2 visit") - - e.GET("/").Expect().Status(httptest.StatusOK). - Body().Contains("3 visit") -} diff --git a/_examples/mvc/singleton/main.go b/_examples/mvc/singleton/main.go deleted file mode 100644 index 971d25810d..0000000000 --- a/_examples/mvc/singleton/main.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "fmt" - "sync/atomic" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" -) - -func main() { - app := iris.New() - mvc.New(app.Party("/")).Handle(&globalVisitorsController{visits: 0}) - - // http://localhost:8080 - app.Listen(":8080") -} - -type globalVisitorsController struct { - // When a singleton controller is used then concurent safe access is up to the developers, because - // all clients share the same controller instance instead. - // Note that any controller's methods - // are per-client, but the struct's field can be shared across multiple clients if the structure - // does not have any dynamic struct field dependencies that depend on the iris.Context - // and ALL field's values are NOT zero, at this case we use uint64 which it's no zero (even if we didn't set it - // manually ease-of-understand reasons) because it's a value of &{0}. - // All the above declares a Singleton, note that you don't have to write a single line of code to do this, Iris is smart enough. - // - // see `Get`. - visits uint64 -} - -func (c *globalVisitorsController) Get() string { - count := atomic.AddUint64(&c.visits, 1) - return fmt.Sprintf("Total visitors: %d", count) -} diff --git a/_examples/mvc/versioned-controller/main.go b/_examples/mvc/versioned-controller/main.go deleted file mode 100644 index 9c8bb95023..0000000000 --- a/_examples/mvc/versioned-controller/main.go +++ /dev/null @@ -1,62 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" -) - -// Optional deprecated X-API-XXX headers for version 1. -var opts = mvc.DeprecationOptions{ - WarnMessage: "deprecated, see ", - DeprecationDate: time.Now().UTC(), - DeprecationInfo: "a bigger version is available, see for more information", -} - -func main() { - app := newApp() - - // See main_test.go for request examples. - app.Listen(":8080") -} - -func newApp() *iris.Application { - app := iris.New() - - dataRouter := app.Party("/data") - { - m := mvc.New(dataRouter) - - m.Handle(new(v1Controller), mvc.Version("1"), mvc.Deprecated(opts)) // 1 or 1.0, 1.0.0 ... - m.Handle(new(v2Controller), mvc.Version("2.3")) // 2.3 or 2.3.0 - m.Handle(new(v3Controller), mvc.Version(">=3, <4")) // 3, 3.x, 3.x.x ... - m.Handle(new(noVersionController)) // or if missing it will respond with 501 version not found. - } - - return app -} - -type v1Controller struct{} - -func (c *v1Controller) Get() string { - return "data (v1.x)" -} - -type v2Controller struct{} - -func (c *v2Controller) Get() string { - return "data (v2.x)" -} - -type v3Controller struct{} - -func (c *v3Controller) Get() string { - return "data (v3.x)" -} - -type noVersionController struct{} - -func (c *noVersionController) Get() string { - return "data" -} diff --git a/_examples/mvc/versioned-controller/main_test.go b/_examples/mvc/versioned-controller/main_test.go deleted file mode 100644 index 309a14f180..0000000000 --- a/_examples/mvc/versioned-controller/main_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/httptest" - "github.com/kataras/iris/v12/versioning" -) - -func TestVersionedController(t *testing.T) { - app := newApp() - - e := httptest.New(t, app) - e.GET("/data").WithHeader(versioning.AcceptVersionHeaderKey, "1").Expect(). - Status(iris.StatusOK).Body().Equal("data (v1.x)") - e.GET("/data").WithHeader(versioning.AcceptVersionHeaderKey, "2.3.0").Expect(). - Status(iris.StatusOK).Body().Equal("data (v2.x)") - e.GET("/data").WithHeader(versioning.AcceptVersionHeaderKey, "3.1").Expect(). - Status(iris.StatusOK).Body().Equal("data (v3.x)") - - // Test invalid version or no version at all. - e.GET("/data").WithHeader(versioning.AcceptVersionHeaderKey, "4").Expect(). - Status(iris.StatusOK).Body().Equal("data") - e.GET("/data").Expect(). - Status(iris.StatusOK).Body().Equal("data") - - // Test Deprecated (v1) - ex := e.GET("/data").WithHeader(versioning.AcceptVersionHeaderKey, "1.0").Expect() - ex.Status(iris.StatusOK).Body().Equal("data (v1.x)") - ex.Header("X-API-Warn").Equal(opts.WarnMessage) - expectedDateStr := opts.DeprecationDate.Format(app.ConfigurationReadOnly().GetTimeFormat()) - ex.Header("X-API-Deprecation-Date").Equal(expectedDateStr) -} diff --git a/_examples/mvc/vuejs-todo-mvc/README.md b/_examples/mvc/vuejs-todo-mvc/README.md deleted file mode 100644 index fc9845c616..0000000000 --- a/_examples/mvc/vuejs-todo-mvc/README.md +++ /dev/null @@ -1,593 +0,0 @@ -# A Todo MVC Application using Iris and Vue.js - -## Hackernoon Article: https://medium.com/hackernoon/a-todo-mvc-application-using-iris-and-vue-js-5019ff870064 - -Vue.js is a front-end framework for building web applications using javascript. It has a blazing fast Virtual DOM renderer. - -Iris is a back-end framework for building web applications using The Go Programming Language (disclaimer: author here). It's one of the fastest and featured web frameworks out there. We wanna use this to serve our "todo service". - -## The Tools - -Programming Languages are just tools for us, but we need a safe, fast and “cross-platform” programming language to power our service. - -[Go](https://golang.org) is a [rapidly growing](https://www.tiobe.com/tiobe-index/) open source programming language designed for building simple, fast, and reliable software. Take a look [here](https://github.com/golang/go/wiki/GoUsers) which great companies use Go to power their services. - -### Install the Go Programming Language - -Extensive information about downloading & installing Go can be found [here](https://golang.org/dl/). - -[![](https://i3.ytimg.com/vi/9x-pG3lvLi0/hqdefault.jpg)](https://youtu.be/9x-pG3lvLi0) - -> Maybe [Windows](https://www.youtube.com/watch?v=WT5mTznJBS0) or [Mac OS X](https://www.youtube.com/watch?v=5qI8z_lB5Lw) user? - -> The article does not contain an introduction to the language itself, if you’re a newcomer I recommend you to bookmark this article, [learn](https://github.com/golang/go/wiki/Learn) the language’s fundamentals and come back later on. - -## The Dependencies - -Many articles have been written, in the past, that lead developers not to use a web framework because they are useless and "bad". I have to tell you that there is no such thing, it always depends on the (web) framework that you’re going to use. At production environment, we don’t have the time or the experience to code everything that we wanna use in the applications, and if we could are we sure that we can do better and safely than others? In short term: **Good frameworks are helpful tools for any developer, company or startup and "bad" frameworks are waste of time, crystal clear.** - -You’ll need two dependencies: - -1. Vue.js, for our client-side requirements. Download it from [here](https://vuejs.org/), latest v2. -2. The Iris Web Framework, for our server-side requirements. Can be found [here](https://github.com/kataras/iris), latest v12. - -> If you have Go already installed then just execute `go get github.com/kataras/iris/v12@latest` to install the Iris Web Framework. - -## Start - -If we are all in the same page, it’s time to learn how we can create a live todo application that will be easy to deploy and extend even more! - -We're going to use a vue.js todo application which uses browser' -s local storage and doesn't have any user-specified features like live sync between browser's tabs, you can find the original version inside the vue's [docs](https://vuejs.org/v2/examples/todomvc.html). - -Assuming that you know how %GOPATH% works, create an empty folder, i.e "vuejs-todo-mvc" in the %GOPATH%/src directory, there you will create those files: - -- web/public/js/app.js -- web/public/index.html -- todo/item.go -- todo/service.go -- web/controllers/todo_controller.go -- web/main.go - -_Read the comments in the source code, they may be very helpful_ - -### The client-side (vue.js) - -```js -/* file: vuejs-todo-mvc/web/public/js/app.js */ -// Full spec-compliant TodoMVC with Iris -// and hash-based routing in ~200 effective lines of JavaScript. - -var ws; - -((async () => { - const events = { - todos: { - saved: function (ns, msg) { - app.todos = msg.unmarshal() - // or make a new http fetch - // fetchTodos(function (items) { - // app.todos = msg.unmarshal() - // }); - } - } - }; - - const conn = await neffos.dial("ws://localhost:8080/todos/sync", events); - ws = await conn.connect("todos"); -})()).catch(console.error); - -function fetchTodos(onComplete) { - axios.get("/todos").then(response => { - if (response.data === null) { - return; - } - - onComplete(response.data); - }); -} - -var todoStorage = { - fetch: function () { - var todos = []; - fetchTodos(function (items) { - for (var i = 0; i < items.length; i++) { - todos.push(items[i]); - } - }); - return todos; - }, - save: function (todos) { - axios.post("/todos", JSON.stringify(todos)).then(response => { - if (!response.data.success) { - window.alert("saving had a failure"); - return; - } - // console.log("send: save"); - ws.emit("save") - }); - } -} - -// visibility filters -var filters = { - all: function (todos) { - return todos - }, - active: function (todos) { - return todos.filter(function (todo) { - return !todo.completed - }) - }, - completed: function (todos) { - return todos.filter(function (todo) { - return todo.completed - }) - } -} - -// app Vue instance -var app = new Vue({ - // app initial state - data: { - todos: todoStorage.fetch(), - newTodo: '', - editedTodo: null, - visibility: 'all' - }, - - // we will not use the "watch" as it works with the fields like "hasChanges" - // and callbacks to make it true but let's keep things very simple as it's just a small getting started. - // // watch todos change for persistence - // watch: { - // todos: { - // handler: function (todos) { - // if (app.hasChanges) { - // todoStorage.save(todos); - // app.hasChanges = false; - // } - - // }, - // deep: true - // } - // }, - - // computed properties - // http://vuejs.org/guide/computed.html - computed: { - filteredTodos: function () { - return filters[this.visibility](this.todos) - }, - remaining: function () { - return filters.active(this.todos).length - }, - allDone: { - get: function () { - return this.remaining === 0 - }, - set: function (value) { - this.todos.forEach(function (todo) { - todo.completed = value - }) - this.notifyChange(); - } - } - }, - - filters: { - pluralize: function (n) { - return n === 1 ? 'item' : 'items' - } - }, - - // methods that implement data logic. - // note there's no DOM manipulation here at all. - methods: { - notifyChange: function () { - todoStorage.save(this.todos) - }, - addTodo: function () { - var value = this.newTodo && this.newTodo.trim() - if (!value) { - return - } - this.todos.push({ - id: this.todos.length + 1, // just for the client-side. - title: value, - completed: false - }) - this.newTodo = '' - this.notifyChange(); - }, - - completeTodo: function (todo) { - if (todo.completed) { - todo.completed = false; - } else { - todo.completed = true; - } - this.notifyChange(); - }, - removeTodo: function (todo) { - this.todos.splice(this.todos.indexOf(todo), 1) - this.notifyChange(); - }, - - editTodo: function (todo) { - this.beforeEditCache = todo.title - this.editedTodo = todo - }, - - doneEdit: function (todo) { - if (!this.editedTodo) { - return - } - this.editedTodo = null - todo.title = todo.title.trim(); - if (!todo.title) { - this.removeTodo(todo); - } - this.notifyChange(); - }, - - cancelEdit: function (todo) { - this.editedTodo = null - todo.title = this.beforeEditCache - }, - - removeCompleted: function () { - this.todos = filters.active(this.todos); - this.notifyChange(); - } - }, - - // a custom directive to wait for the DOM to be updated - // before focusing on the input field. - // http://vuejs.org/guide/custom-directive.html - directives: { - 'todo-focus': function (el, binding) { - if (binding.value) { - el.focus() - } - } - } -}) - -// handle routing -function onHashChange() { - var visibility = window.location.hash.replace(/#\/?/, '') - if (filters[visibility]) { - app.visibility = visibility - } else { - window.location.hash = '' - app.visibility = 'all' - } -} - -window.addEventListener('hashchange', onHashChange) -onHashChange() - -// mount -app.$mount('.todoapp'); -``` - -Let's add our view, the static html. - -```html - - - - - - - Iris + Vue.js • TodoMVC - - - - - - - - - - - - - -
-
-

todos

- -
-
- -
    -
  • -
    - - - - -
    - -
  • -
-
-
- - {{ remaining }} {{ remaining | pluralize }} left - - - -
-
-
-

Double-click to edit a todo

-
- - - - - -``` - -### The server-side (iris) - -Our view model. - -```go -// file: vuejs-todo-mvc/todo/item.go -package todo - -type Item struct { - SessionID string `json:"-"` - ID int64 `json:"id,omitempty"` - Title string `json:"title"` - Completed bool `json:"completed"` -} -``` - -Our service. - -```go -// file: vuejs-todo-mvc/todo/service.go -package todo - -import ( - "sync" -) - -type Service interface { - Get(owner string) []Item - Save(owner string, newItems []Item) error -} - -type MemoryService struct { - // key = session id, value the list of todo items that this session id has. - items map[string][]Item - // protected by locker for concurrent access. - mu sync.RWMutex -} - -func NewMemoryService() *MemoryService { - return &MemoryService{ - items: make(map[string][]Item, 0), - } -} - -func (s *MemoryService) Get(sessionOwner string) []Item { - s.mu.RLock() - items := s.items[sessionOwner] - s.mu.RUnlock() - - return items -} - -func (s *MemoryService) Save(sessionOwner string, newItems []Item) error { - var prevID int64 - for i := range newItems { - if newItems[i].ID == 0 { - newItems[i].ID = prevID - prevID++ - } - } - - s.mu.Lock() - s.items[sessionOwner] = newItems - s.mu.Unlock() - return nil -} -``` - -We are going to use some of the MVC functionalities of the iris web framework here but you can do the same with the standard API as well. - -```go -// file: vuejs-todo-mvc/web/controllers/todo_controller.go -package controllers - -import ( - "vuejs-todo-mvc/todo" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" - "github.com/kataras/iris/v12/sessions" - "github.com/kataras/iris/v12/websocket" -) - -// TodoController is our TODO app's web controller. -type TodoController struct { - Service todo.Service - - Session *sessions.Session - - NS *websocket.NSConn -} - -// BeforeActivation called once before the server ran, and before -// the routes and dependencies binded. -// You can bind custom things to the controller, add new methods, add middleware, -// add dependencies to the struct or the method(s) and more. -func (c *TodoController) BeforeActivation(b mvc.BeforeActivation) { - // this could be binded to a controller's function input argument - // if any, or struct field if any: - b.Dependencies().Add(func(ctx iris.Context) (items []todo.Item) { - ctx.ReadJSON(&items) - return - }) -} - -// Get handles the GET: /todos route. -func (c *TodoController) Get() []todo.Item { - return c.Service.Get(c.Session.ID()) -} - -// PostItemResponse the response data that will be returned as json -// after a post save action of all todo items. -type PostItemResponse struct { - Success bool `json:"success"` -} - -var emptyResponse = PostItemResponse{Success: false} - -// Post handles the POST: /todos route. -func (c *TodoController) Post(newItems []todo.Item) PostItemResponse { - if err := c.Service.Save(c.Session.ID(), newItems); err != nil { - return emptyResponse - } - - return PostItemResponse{Success: true} -} - -func (c *TodoController) Save(msg websocket.Message) error { - id := c.Session.ID() - c.NS.Conn.Server().Broadcast(nil, websocket.Message{ - Namespace: msg.Namespace, - Event: "saved", - To: id, - Body: websocket.Marshal(c.Service.Get(id)), - }) - - return nil -} - -``` - -And finally our main application's endpoint. - -```go -// file: web/main.go -package main - -import ( - "strings" - - "github.com/kataras/iris/v12/_examples/mvc/vuejs-todo-mvc/src/todo" - "github.com/kataras/iris/v12/_examples/mvc/vuejs-todo-mvc/src/web/controllers" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" - "github.com/kataras/iris/v12/sessions" - "github.com/kataras/iris/v12/websocket" -) - -func main() { - app := iris.New() - - // serve our app in public, public folder - // contains the client-side vue.js application, - // no need for any server-side template here, - // actually if you're going to just use vue without any - // back-end services, you can just stop afer this line and start the server. - app.HandleDir("/", iris.Dir("./public")) - - // configure the http sessions. - sess := sessions.New(sessions.Config{ - Cookie: "iris_session", - }) - - // create a sub router and register the http controllers. - todosRouter := app.Party("/todos") - - // create our mvc application targeted to /todos relative sub path. - todosApp := mvc.New(todosRouter) - - // any dependencies bindings here... - todosApp.Register( - todo.NewMemoryService(), - ) - - todosController := new(controllers.TodoController) - // controllers registration here... - todosApp.Handle(todosController) - - // Create a sub mvc app for websocket controller. - // Inherit the parent's dependencies. - todosWebsocketApp := todosApp.Party("/sync") - todosWebsocketApp.HandleWebsocket(todosController). - SetNamespace("todos"). - SetEventMatcher(func(methodName string) (string, bool) { - return strings.ToLower(methodName), true - }) - - websocketServer := websocket.New(websocket.DefaultGorillaUpgrader, todosWebsocketApp) - idGenerator := func(ctx iris.Context) string { - id := sess.Start(ctx).ID() - return id - } - todosWebsocketApp.Router.Get("/", websocket.Handler(websocketServer, idGenerator)) - - // start the web server at http://localhost:8080 - app.Listen(":8080") -} -``` - -Run the Iris web server you've just created by executing `go run main.go` from your current path (%GOPATH%/src/%your_folder%/web/). - -```sh -$ go run main.go -Now listening on: http://localhost:8080 -Application Started. Press CTRL+C to shut down. -_ -``` - -Open one or more browser tabs at: http://localhost:8080 and have fun! - -![](screen.png) - -### Download the Source Code - -The whole project, all the files you saw in this article are located at: https://github.com/kataras/iris/tree/master/_examples/mvc/vuejs-todo-mvc - -## References - -https://vuejs.org/v2/examples/todomvc.html (using browser's local storage) - -https://github.com/kataras/iris/tree/master/_examples/mvc (mvc examples and features overview repository) - -## Thank you, once again - -Happy new year and thank you for your pattience, once again:) Don't hesitate to post any questions and provide feedback(I'm very active dev therefore you will be heard here!) - -Don't forget to check out my medium profile and twitter as well, I'm posting some (useful) stuff there too:) - -- https://medium.com/@kataras -- https://twitter.com/MakisMaropoulos diff --git a/_examples/mvc/vuejs-todo-mvc/screen.png b/_examples/mvc/vuejs-todo-mvc/screen.png deleted file mode 100644 index 41df240d7f..0000000000 Binary files a/_examples/mvc/vuejs-todo-mvc/screen.png and /dev/null differ diff --git a/_examples/mvc/vuejs-todo-mvc/src/todo/item.go b/_examples/mvc/vuejs-todo-mvc/src/todo/item.go deleted file mode 100644 index eb464be25c..0000000000 --- a/_examples/mvc/vuejs-todo-mvc/src/todo/item.go +++ /dev/null @@ -1,8 +0,0 @@ -package todo - -type Item struct { - SessionID string `json:"-"` - ID int64 `json:"id,omitempty"` - Title string `json:"title"` - Completed bool `json:"completed"` -} diff --git a/_examples/mvc/vuejs-todo-mvc/src/todo/service.go b/_examples/mvc/vuejs-todo-mvc/src/todo/service.go deleted file mode 100644 index d9af2854b1..0000000000 --- a/_examples/mvc/vuejs-todo-mvc/src/todo/service.go +++ /dev/null @@ -1,46 +0,0 @@ -package todo - -import ( - "sync" -) - -type Service interface { - Get(owner string) []Item - Save(owner string, newItems []Item) error -} - -type MemoryService struct { - // key = session id, value the list of todo items that this session id has. - items map[string][]Item - // protected by locker for concurrent access. - mu sync.RWMutex -} - -func NewMemoryService() *MemoryService { - return &MemoryService{ - items: make(map[string][]Item, 0), - } -} - -func (s *MemoryService) Get(sessionOwner string) []Item { - s.mu.RLock() - items := s.items[sessionOwner] - s.mu.RUnlock() - - return items -} - -func (s *MemoryService) Save(sessionOwner string, newItems []Item) error { - var prevID int64 - for i := range newItems { - if newItems[i].ID == 0 { - newItems[i].ID = prevID - prevID++ - } - } - - s.mu.Lock() - s.items[sessionOwner] = newItems - s.mu.Unlock() - return nil -} diff --git a/_examples/mvc/vuejs-todo-mvc/src/web/controllers/todo_controller.go b/_examples/mvc/vuejs-todo-mvc/src/web/controllers/todo_controller.go deleted file mode 100644 index 407b8eefa7..0000000000 --- a/_examples/mvc/vuejs-todo-mvc/src/web/controllers/todo_controller.go +++ /dev/null @@ -1,66 +0,0 @@ -package controllers - -import ( - "github.com/kataras/iris/v12/_examples/mvc/vuejs-todo-mvc/src/todo" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" - "github.com/kataras/iris/v12/sessions" - "github.com/kataras/iris/v12/websocket" -) - -// TodoController is our TODO app's web controller. -type TodoController struct { - Service todo.Service - - Session *sessions.Session - - NS *websocket.NSConn -} - -// BeforeActivation called once before the server ran, and before -// the routes and dependencies binded. -// You can bind custom things to the controller, add new methods, add middleware, -// add dependencies to the struct or the method(s) and more. -func (c *TodoController) BeforeActivation(b mvc.BeforeActivation) { - // this could be binded to a controller's function input argument - // if any, or struct field if any: - b.Dependencies().Register(func(ctx iris.Context) (items []todo.Item) { - ctx.ReadJSON(&items) - return - }) // Note: from Iris v12.2 these type of dependencies are automatically resolved. -} - -// Get handles the GET: /todos route. -func (c *TodoController) Get() []todo.Item { - return c.Service.Get(c.Session.ID()) -} - -// PostItemResponse the response data that will be returned as json -// after a post save action of all todo items. -type PostItemResponse struct { - Success bool `json:"success"` -} - -var emptyResponse = PostItemResponse{Success: false} - -// Post handles the POST: /todos route. -func (c *TodoController) Post(newItems []todo.Item) PostItemResponse { - if err := c.Service.Save(c.Session.ID(), newItems); err != nil { - return emptyResponse - } - - return PostItemResponse{Success: true} -} - -func (c *TodoController) Save(msg websocket.Message) error { - id := c.Session.ID() - c.NS.Conn.Server().Broadcast(nil, websocket.Message{ - Namespace: msg.Namespace, - Event: "saved", - To: id, - Body: websocket.Marshal(c.Service.Get(id)), - }) - - return nil -} diff --git a/_examples/mvc/vuejs-todo-mvc/src/web/main.go b/_examples/mvc/vuejs-todo-mvc/src/web/main.go deleted file mode 100644 index 8a2d54e61b..0000000000 --- a/_examples/mvc/vuejs-todo-mvc/src/web/main.go +++ /dev/null @@ -1,63 +0,0 @@ -package main - -import ( - "strings" - - "github.com/kataras/iris/v12/_examples/mvc/vuejs-todo-mvc/src/todo" - "github.com/kataras/iris/v12/_examples/mvc/vuejs-todo-mvc/src/web/controllers" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/mvc" - "github.com/kataras/iris/v12/sessions" - "github.com/kataras/iris/v12/websocket" -) - -func main() { - app := iris.New() - - // serve our app in public, public folder - // contains the client-side vue.js application, - // no need for any server-side template here, - // actually if you're going to just use vue without any - // back-end services, you can just stop afer this line and start the server. - app.HandleDir("/", iris.Dir("./public")) - - // configure the http sessions. - sess := sessions.New(sessions.Config{ - Cookie: "iris_session", - }) - - // create a sub router and register the http controllers. - todosRouter := app.Party("/todos") - - // create our mvc application targeted to /todos relative sub path. - todosApp := mvc.New(todosRouter) - - // any dependencies bindings here... - todosApp.Register( - todo.NewMemoryService(), - ) - - todosController := new(controllers.TodoController) - // controllers registration here... - todosApp.Handle(todosController) - - // Create a sub mvc app for websocket controller. - // Inherit the parent's dependencies. - todosWebsocketApp := todosApp.Party("/sync") - todosWebsocketApp.HandleWebsocket(todosController). - SetNamespace("todos"). - SetEventMatcher(func(methodName string) (string, bool) { - return strings.ToLower(methodName), true - }) - - websocketServer := websocket.New(websocket.DefaultGorillaUpgrader, todosWebsocketApp) - idGenerator := func(ctx iris.Context) string { - id := sess.Start(ctx).ID() - return id - } - todosWebsocketApp.Router.Get("/", websocket.Handler(websocketServer, idGenerator)) - - // start the web server at http://localhost:8080 - app.Listen(":8080") -} diff --git a/_examples/mvc/vuejs-todo-mvc/src/web/public/css/index b/_examples/mvc/vuejs-todo-mvc/src/web/public/css/index deleted file mode 100644 index 3aea0097d7..0000000000 --- a/_examples/mvc/vuejs-todo-mvc/src/web/public/css/index +++ /dev/null @@ -1,2 +0,0 @@ -index.css is not here to reduce the disk space for the examples. -https://unpkg.com/todomvc-app-css@2.0.4/index.css is used instead. \ No newline at end of file diff --git a/_examples/mvc/vuejs-todo-mvc/src/web/public/index.html b/_examples/mvc/vuejs-todo-mvc/src/web/public/index.html deleted file mode 100644 index 6b43069d1f..0000000000 --- a/_examples/mvc/vuejs-todo-mvc/src/web/public/index.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - Iris + Vue.js • TodoMVC - - - - - - - - - - - - - -
-
-

todos

- -
-
- -
    -
  • -
    - - - - -
    - -
  • -
-
-
- - {{ remaining }} {{ remaining | pluralize }} left - - - -
-
-
-

Double-click to edit a todo

-
- - - - - \ No newline at end of file diff --git a/_examples/mvc/vuejs-todo-mvc/src/web/public/js/app.js b/_examples/mvc/vuejs-todo-mvc/src/web/public/js/app.js deleted file mode 100644 index fcc0863d2c..0000000000 --- a/_examples/mvc/vuejs-todo-mvc/src/web/public/js/app.js +++ /dev/null @@ -1,214 +0,0 @@ -// Full spec-compliant TodoMVC with Iris -// and hash-based routing in ~200 effective lines of JavaScript. - -var ws; - -((async () => { - const events = { - todos: { - saved: function (ns, msg) { - app.todos = msg.unmarshal() - // or make a new http fetch - // fetchTodos(function (items) { - // app.todos = msg.unmarshal() - // }); - } - } - }; - - const conn = await neffos.dial("ws://localhost:8080/todos/sync", events); - ws = await conn.connect("todos"); -})()).catch(console.error); - -function fetchTodos(onComplete) { - axios.get("/todos").then(response => { - if (response.data === null) { - return; - } - - onComplete(response.data); - }); -} - -var todoStorage = { - fetch: function () { - var todos = []; - fetchTodos(function (items) { - for (var i = 0; i < items.length; i++) { - todos.push(items[i]); - } - }); - return todos; - }, - save: function (todos) { - axios.post("/todos", JSON.stringify(todos)).then(response => { - if (!response.data.success) { - window.alert("saving had a failure"); - return; - } - // console.log("send: save"); - ws.emit("save") - }); - } -} - -// visibility filters -var filters = { - all: function (todos) { - return todos - }, - active: function (todos) { - return todos.filter(function (todo) { - return !todo.completed - }) - }, - completed: function (todos) { - return todos.filter(function (todo) { - return todo.completed - }) - } -} - -// app Vue instance -var app = new Vue({ - // app initial state - data: { - todos: todoStorage.fetch(), - newTodo: '', - editedTodo: null, - visibility: 'all' - }, - - // we will not use the "watch" as it works with the fields like "hasChanges" - // and callbacks to make it true but let's keep things very simple as it's just a small getting started. - // // watch todos change for persistence - // watch: { - // todos: { - // handler: function (todos) { - // if (app.hasChanges) { - // todoStorage.save(todos); - // app.hasChanges = false; - // } - - // }, - // deep: true - // } - // }, - - // computed properties - // http://vuejs.org/guide/computed.html - computed: { - filteredTodos: function () { - return filters[this.visibility](this.todos) - }, - remaining: function () { - return filters.active(this.todos).length - }, - allDone: { - get: function () { - return this.remaining === 0 - }, - set: function (value) { - this.todos.forEach(function (todo) { - todo.completed = value - }) - this.notifyChange(); - } - } - }, - - filters: { - pluralize: function (n) { - return n === 1 ? 'item' : 'items' - } - }, - - // methods that implement data logic. - // note there's no DOM manipulation here at all. - methods: { - notifyChange: function () { - todoStorage.save(this.todos) - }, - addTodo: function () { - var value = this.newTodo && this.newTodo.trim() - if (!value) { - return - } - this.todos.push({ - id: this.todos.length + 1, // just for the client-side. - title: value, - completed: false - }) - this.newTodo = '' - this.notifyChange(); - }, - - completeTodo: function (todo) { - if (todo.completed) { - todo.completed = false; - } else { - todo.completed = true; - } - this.notifyChange(); - }, - removeTodo: function (todo) { - this.todos.splice(this.todos.indexOf(todo), 1) - this.notifyChange(); - }, - - editTodo: function (todo) { - this.beforeEditCache = todo.title - this.editedTodo = todo - }, - - doneEdit: function (todo) { - if (!this.editedTodo) { - return - } - this.editedTodo = null - todo.title = todo.title.trim(); - if (!todo.title) { - this.removeTodo(todo); - } - this.notifyChange(); - }, - - cancelEdit: function (todo) { - this.editedTodo = null - todo.title = this.beforeEditCache - }, - - removeCompleted: function () { - this.todos = filters.active(this.todos); - this.notifyChange(); - } - }, - - // a custom directive to wait for the DOM to be updated - // before focusing on the input field. - // http://vuejs.org/guide/custom-directive.html - directives: { - 'todo-focus': function (el, binding) { - if (binding.value) { - el.focus() - } - } - } -}) - -// handle routing -function onHashChange() { - var visibility = window.location.hash.replace(/#\/?/, '') - if (filters[visibility]) { - app.visibility = visibility - } else { - window.location.hash = '' - app.visibility = 'all' - } -} - -window.addEventListener('hashchange', onHashChange) -onHashChange() - -// mount -app.$mount('.todoapp'); \ No newline at end of file diff --git a/_examples/mvc/vuejs-todo-mvc/src/web/public/js/lib/vue b/_examples/mvc/vuejs-todo-mvc/src/web/public/js/lib/vue deleted file mode 100644 index 8da03f4896..0000000000 --- a/_examples/mvc/vuejs-todo-mvc/src/web/public/js/lib/vue +++ /dev/null @@ -1,2 +0,0 @@ -vue.js is not here to reduce the disk space for the examples. -Instead https://vuejs.org/js/vue.js is used instead. \ No newline at end of file diff --git a/_examples/mvc/websocket/browser/index.html b/_examples/mvc/websocket/browser/index.html deleted file mode 100644 index c7a2f910a1..0000000000 --- a/_examples/mvc/websocket/browser/index.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - Online visitors MVC example - - - - -
- 1 online visitor -
- - - - - - - - -

-    
-    
-    
-
-
-
-
\ No newline at end of file
diff --git a/_examples/mvc/websocket/main.go b/_examples/mvc/websocket/main.go
deleted file mode 100644
index 710760161d..0000000000
--- a/_examples/mvc/websocket/main.go
+++ /dev/null
@@ -1,118 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"sync/atomic"
-
-	"github.com/kataras/iris/v12"
-	"github.com/kataras/iris/v12/mvc"
-	"github.com/kataras/iris/v12/websocket"
-)
-
-func main() {
-	app := iris.New()
-	app.Logger().SetLevel("debug")
-
-	// load templates.
-	// app.RegisterView(iris.HTML("./views", ".html"))
-
-	// render the ./browser/index.html.
-	app.HandleDir("/", iris.Dir("./browser"))
-
-	websocketAPI := app.Party("/websocket")
-
-	m := mvc.New(websocketAPI)
-	m.Register(
-		&prefixedLogger{prefix: "DEV"},
-	)
-	m.HandleWebsocket(&websocketController{Namespace: "default", Age: 42, Otherstring: "other string"})
-
-	websocketServer := websocket.New(websocket.DefaultGorillaUpgrader, m)
-
-	websocketAPI.Get("/", websocket.Handler(websocketServer))
-	// http://localhost:8080
-	app.Listen(":8080")
-}
-
-var visits uint64
-
-func increment() uint64 {
-	return atomic.AddUint64(&visits, 1)
-}
-
-func decrement() uint64 {
-	return atomic.AddUint64(&visits, ^uint64(0))
-}
-
-type websocketController struct {
-	*websocket.NSConn `stateless:"true"`
-	Namespace         string
-	Age               int
-	Otherstring       string
-
-	Logger LoggerService
-}
-
-// or
-// func (c *websocketController) Namespace() string {
-// 	return "default"
-// }
-
-func (c *websocketController) OnNamespaceDisconnect(msg websocket.Message) error {
-	c.Logger.Log("Disconnected")
-	// visits--
-	newCount := decrement()
-	// This will call the "OnVisit" event on all clients, except the current one,
-	// (it can't because it's left but for any case use this type of design)
-	c.Conn.Server().Broadcast(nil, websocket.Message{
-		Namespace: msg.Namespace,
-		Event:     "OnVisit",
-		Body:      []byte(fmt.Sprintf("%d", newCount)),
-	})
-
-	return nil
-}
-
-func (c *websocketController) OnNamespaceConnected(msg websocket.Message) error {
-	// println("Broadcast prefix is: " + c.BroadcastPrefix)
-	c.Logger.Log("Connected")
-
-	// visits++
-	newCount := increment()
-
-	// This will call the "OnVisit" event on all clients, including the current one,
-	// with the 'newCount' variable.
-	//
-	// There are many ways that u can do it and faster, for example u can just send a new visitor
-	// and client can increment itself, but here we are just "showcasing" the websocket controller.
-	c.Conn.Server().Broadcast(nil, websocket.Message{
-		Namespace: msg.Namespace,
-		Event:     "OnVisit",
-		Body:      []byte(fmt.Sprintf("%d", newCount)),
-	})
-
-	return nil
-}
-
-func (c *websocketController) OnChat(msg websocket.Message) error {
-	ctx := websocket.GetContext(c.Conn)
-
-	ctx.Application().Logger().Infof("[IP: %s] [ID: %s]  broadcast to other clients the message [%s]",
-		ctx.RemoteAddr(), c, string(msg.Body))
-
-	c.Conn.Server().Broadcast(c, msg)
-
-	return nil
-}
-
-type LoggerService interface {
-	Log(string)
-}
-
-type prefixedLogger struct {
-	prefix string
-}
-
-func (s *prefixedLogger) Log(msg string) {
-	fmt.Printf("%s: %s\n", s.prefix, msg)
-}
diff --git a/_examples/pprof/main.go b/_examples/pprof/main.go
deleted file mode 100644
index 47705503a0..0000000000
--- a/_examples/pprof/main.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package main
-
-import (
-	"github.com/kataras/iris/v12"
-
-	"github.com/kataras/iris/v12/middleware/pprof"
-)
-
-func main() {
-	app := iris.New()
-
-	app.Get("/", func(ctx iris.Context) {
-		ctx.HTML("

Please click here") - }) - - p := pprof.New() - app.Any("/debug/pprof", p) - app.Any("/debug/pprof/{action:path}", p) - // ___________ - app.Listen(":8080") -} diff --git a/_examples/recover/main.go b/_examples/recover/main.go deleted file mode 100644 index 7825180d9a..0000000000 --- a/_examples/recover/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - - "github.com/kataras/iris/v12/middleware/recover" -) - -func main() { - app := iris.New() - app.Use(recover.New()) - - i := 0 - // let's simulate a panic every next request - app.Get("/", func(ctx iris.Context) { - i++ - if i%2 == 0 { - panic("a panic here") - } - ctx.Writef("Hello, refresh one time more to get panic!") - }) - - // http://localhost:8080, refresh it 5-6 times. - app.Listen(":8080") -} - -// Note: -// app := iris.Default() instead of iris.New() makes use of the recovery middleware automatically. diff --git a/_examples/request-body/read-body/main.go b/_examples/request-body/read-body/main.go deleted file mode 100644 index 001edca498..0000000000 --- a/_examples/request-body/read-body/main.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := newApp() - // See main_test.go for usage. - app.Listen(":8080") -} - -func newApp() *iris.Application { - app := iris.New() - // To automatically decompress using gzip: - // app.Use(iris.GzipReader) - - app.Use(setAllowedResponses) - - app.Post("/", readBody) - - return app -} - -type payload struct { - Message string `json:"message" xml:"message" msgpack:"message" yaml:"Message" url:"message" form:"message"` -} - -func readBody(ctx iris.Context) { - var p payload - - // Bind request body to "p" depending on the content-type that client sends the data, - // e.g. JSON, XML, YAML, MessagePack, Form, URL Query. - err := ctx.ReadBody(&p) - if err != nil { - ctx.StopWithProblem(iris.StatusBadRequest, - iris.NewProblem().Title("Parser issue").Detail(err.Error())) - return - } - - // For the sake of the example, log the received payload. - ctx.Application().Logger().Infof("Received: %#+v", p) - - // Send back the payload depending on the accept content type and accept-encoding of the client, - // e.g. JSON, XML and so on. - ctx.Negotiate(p) -} - -func setAllowedResponses(ctx iris.Context) { - // Indicate that the Server can send JSON, XML, YAML and MessagePack for this request. - ctx.Negotiation().JSON().XML().YAML().MsgPack() - // Add more, allowed by the server format of responses, mime types here... - - // If client is missing an "Accept: " header then default it to JSON. - ctx.Negotiation().Accept.JSON() - - ctx.Next() -} diff --git a/_examples/request-body/read-body/main_test.go b/_examples/request-body/read-body/main_test.go deleted file mode 100644 index 6522c9abcf..0000000000 --- a/_examples/request-body/read-body/main_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestReadBodyAndNegotiate(t *testing.T) { - app := newApp() - - e := httptest.New(t, app) - - var ( - expectedPayload = payload{Message: "a message"} - expectedMsgPackPayload = "\x81\xa7message\xa9a message" - expectedXMLPayload = ` - a message - -` - expectedYAMLPayload = "Message: a message\n" - ) - - // Test send JSON and receive JSON. - e.POST("/").WithJSON(expectedPayload).Expect().Status(httptest.StatusOK). - JSON().Equal(expectedPayload) - - // Test send Form and receive XML. - e.POST("/").WithForm(expectedPayload). - WithHeader("Accept", "application/xml"). - Expect().Status(httptest.StatusOK). - Body().Equal(expectedXMLPayload) - - // Test send URL Query and receive MessagePack. - e.POST("/").WithQuery("message", expectedPayload.Message). - WithHeader("Accept", "application/msgpack"). - Expect().Status(httptest.StatusOK).ContentType("application/msgpack"). - Body().Equal(expectedMsgPackPayload) - - // Test send MessagePack and receive MessagePack. - e.POST("/").WithBytes([]byte(expectedMsgPackPayload)). - WithHeader("Content-Type", "application/msgpack"). - WithHeader("Accept", "application/msgpack"). - Expect().Status(httptest.StatusOK). - ContentType("application/msgpack").Body().Equal(expectedMsgPackPayload) - - // Test send YAML and receive YAML. - e.POST("/").WithBytes([]byte(expectedYAMLPayload)). - WithHeader("Content-Type", "application/x-yaml"). - WithHeader("Accept", "application/x-yaml"). - Expect().Status(httptest.StatusOK). - ContentType("application/x-yaml").Body().Equal(expectedYAMLPayload) -} diff --git a/_examples/request-body/read-custom-per-type/main.go b/_examples/request-body/read-custom-per-type/main.go deleted file mode 100644 index e60b3429ec..0000000000 --- a/_examples/request-body/read-custom-per-type/main.go +++ /dev/null @@ -1,64 +0,0 @@ -package main - -import ( - "gopkg.in/yaml.v3" - - "github.com/kataras/iris/v12" -) - -func main() { - app := newApp() - - // use Postman or whatever to do a POST request - // (however you are always free to use app.Get and GET http method requests to read body of course) - // to the http://localhost:8080 with RAW BODY: - /* - addr: localhost:8080 - serverName: Iris - */ - // - // The response should be: - // Received: main.config{Addr:"localhost:8080", ServerName:"Iris"} - app.Listen(":8080", iris.WithOptimizations) -} - -func newApp() *iris.Application { - app := iris.New() - app.Post("/", handler) - - return app -} - -// simple yaml stuff, read more at https://github.com/go-yaml/yaml -type config struct { - Addr string `yaml:"addr"` - ServerName string `yaml:"serverName"` -} - -// Decode implements the `kataras/iris/context#BodyDecoder` optional interface -// that any go type can implement in order to be self-decoded when reading the request's body. -func (c *config) Decode(body []byte) error { - return yaml.Unmarshal(body, c) -} - -func handler(ctx iris.Context) { - var c config - - // - // Note: - // second parameter is nil because our &c implements the `context#BodyDecoder` - // which has a priority over the context#Unmarshaler (which can be a more global option for reading request's body) - // see the `request-body/read-custom-via-unmarshaler/main.go` example to learn how to use the context#Unmarshaler too. - // - // Note 2: - // If you need to read the body again for any reason - // you should disable the body consumption via `app.Run(..., iris.WithoutBodyConsumptionOnUnmarshal)`. - // - - if err := ctx.UnmarshalBody(&c, nil); err != nil { - ctx.StopWithError(iris.StatusBadRequest, err) - return - } - - ctx.Writef("Received: %#+v", c) -} diff --git a/_examples/request-body/read-custom-per-type/main_test.go b/_examples/request-body/read-custom-per-type/main_test.go deleted file mode 100644 index 70b96c04ad..0000000000 --- a/_examples/request-body/read-custom-per-type/main_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestReadCustomPerType(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - expectedResponse := `Received: main.config{Addr:"localhost:8080", ServerName:"Iris"}` - - e.POST("/").WithText("addr: localhost:8080\nserverName: Iris").Expect(). - Status(httptest.StatusOK).Body().Equal(expectedResponse) -} diff --git a/_examples/request-body/read-custom-via-unmarshaler/main.go b/_examples/request-body/read-custom-via-unmarshaler/main.go deleted file mode 100644 index ad0d133271..0000000000 --- a/_examples/request-body/read-custom-via-unmarshaler/main.go +++ /dev/null @@ -1,72 +0,0 @@ -package main - -import ( - "gopkg.in/yaml.v3" - - "github.com/kataras/iris/v12" -) - -func main() { - app := newApp() - - // use Postman or whatever to do a POST request - // (however you are always free to use app.Get and GET http method requests to read body of course) - // to the http://localhost:8080 with RAW BODY: - /* - addr: localhost:8080 - serverName: Iris - */ - // - // The response should be: - // Received: main.config{Addr:"localhost:8080", ServerName:"Iris"} - app.Listen(":8080", iris.WithOptimizations) -} - -func newApp() *iris.Application { - app := iris.New() - app.Post("/", handler) - - return app -} - -// simple yaml stuff, read more at https://github.com/go-yaml/yaml -type config struct { - Addr string `yaml:"addr"` - ServerName string `yaml:"serverName"` -} - -/* -type myBodyDecoder struct{} - -var DefaultBodyDecoder = myBodyDecoder{} - -// Implements the `kataras/iris/context#Unmarshaler` but at our example -// we will use the simplest `context#UnmarshalerFunc` to pass just the yaml.Unmarshal. -// -// Can be used as: ctx.UnmarshalBody(&c, DefaultBodyDecoder) -func (r *myBodyDecoder) Unmarshal(data []byte, outPtr interface{}) error { - return yaml.Unmarshal(data, outPtr) -} -*/ - -func handler(ctx iris.Context) { - var c config - - // - // Note: - // yaml.Unmarshal already implements the `context#Unmarshaler` - // so we can use it directly, like the json.Unmarshal(ctx.ReadJSON), xml.Unmarshal(ctx.ReadXML) - // and every library which follows the best practises and is aligned with the Go standards. - // - // Note 2: - // If you need to read the body again for any reason - // you should disable the body consumption via `app.Run(..., iris.WithoutBodyConsumptionOnUnmarshal)`. - // - - if err := ctx.UnmarshalBody(&c, iris.UnmarshalerFunc(yaml.Unmarshal)); err != nil { - ctx.StopWithError(iris.StatusBadRequest, err) - return - } - - ctx.Writef("Received: %#+v", c) -} diff --git a/_examples/request-body/read-custom-via-unmarshaler/main_test.go b/_examples/request-body/read-custom-via-unmarshaler/main_test.go deleted file mode 100644 index 75efdd768a..0000000000 --- a/_examples/request-body/read-custom-via-unmarshaler/main_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestReadCustomViaUnmarshaler(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - expectedResponse := `Received: main.config{Addr:"localhost:8080", ServerName:"Iris"}` - - e.POST("/").WithText("addr: localhost:8080\nserverName: Iris").Expect(). - Status(httptest.StatusOK).Body().Equal(expectedResponse) -} diff --git a/_examples/request-body/read-form/main.go b/_examples/request-body/read-form/main.go deleted file mode 100644 index cb82ffe487..0000000000 --- a/_examples/request-body/read-form/main.go +++ /dev/null @@ -1,47 +0,0 @@ -// package main contains an example on how to use the ReadForm, but with the same way you can do the ReadJSON & ReadJSON -package main - -import ( - "github.com/kataras/iris/v12" -) - -type Visitor struct { - Username string - Mail string - Data []string `form:"mydata"` -} - -func main() { - app := iris.New() - - // set the view html template engine - app.RegisterView(iris.HTML("./templates", ".html").Reload(true)) - - app.Get("/", func(ctx iris.Context) { - if err := ctx.View("form.html"); err != nil { - ctx.StopWithError(iris.StatusInternalServerError, err) - return - } - }) - - app.Post("/form_action", func(ctx iris.Context) { - visitor := Visitor{} - err := ctx.ReadForm(&visitor) - if err != nil { - if !iris.IsErrPath(err) /* see: https://github.com/kataras/iris/issues/1157 */ || - err == iris.ErrEmptyForm { - ctx.StopWithError(iris.StatusInternalServerError, err) - return - } - } - - ctx.Writef("Visitor: %#v", visitor) - }) - - app.Post("/post_value", func(ctx iris.Context) { - username := ctx.PostValueDefault("Username", "iris") - ctx.Writef("Username: %s", username) - }) - - app.Listen(":8080", iris.WithEmptyFormError /* returns ErrEmptyForm if the request form body was empty */) -} diff --git a/_examples/request-body/read-form/templates/form.html b/_examples/request-body/read-form/templates/form.html deleted file mode 100644 index 7f85b96283..0000000000 --- a/_examples/request-body/read-form/templates/form.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - -
- Username:
- Mail:
- Select one or more:
- - -
- - -
- - diff --git a/_examples/request-body/read-json-struct-validation/main.go b/_examples/request-body/read-json-struct-validation/main.go deleted file mode 100644 index 294931837f..0000000000 --- a/_examples/request-body/read-json-struct-validation/main.go +++ /dev/null @@ -1,146 +0,0 @@ -// Package main shows the validator(latest, version 10) integration with Iris' Context methods of -// `ReadJSON`, `ReadXML`, `ReadMsgPack`, `ReadYAML`, `ReadForm`, `ReadQuery`, `ReadBody`. -// -// You can find more examples of this 3rd-party library at: -// https://github.com/go-playground/validator/blob/master/_examples -package main - -import ( - "fmt" - - "github.com/kataras/iris/v12" - - // $ go get github.com/go-playground/validator/v10@latest - "github.com/go-playground/validator/v10" -) - -func main() { - app := iris.New() - app.Validator = validator.New() - - userRouter := app.Party("/user") - { - userRouter.Get("/validation-errors", resolveErrorsDocumentation) - userRouter.Post("/", postUser) - } - - // Use Postman or any tool to perform a POST request - // to the http://localhost:8080/user with RAW BODY of: - /* - { - "fname": "", - "lname": "", - "age": 45, - "email": "mail@example.com", - "favColor": "#000", - "addresses": [{ - "street": "Eavesdown Docks", - "planet": "Persphone", - "phone": "none", - "city": "Unknown" - }] - } - */ - /* The response should be: - { - "title": "Validation error", - "detail": "One or more fields failed to be validated", - "type": "http://localhost:8080/user/validation-errors", - "status": 400, - "fields": [ - { - "tag": "required", - "namespace": "User.FirstName", - "kind": "string", - "type": "string", - "value": "", - "param": "" - }, - { - "tag": "required", - "namespace": "User.LastName", - "kind": "string", - "type": "string", - "value": "", - "param": "" - } - ] - } - */ - app.Listen(":8080") -} - -// User contains user information. -type User struct { - FirstName string `json:"fname" validate:"required"` - LastName string `json:"lname" validate:"required"` - Age uint8 `json:"age" validate:"gte=0,lte=130"` - Email string `json:"email" validate:"required,email"` - FavouriteColor string `json:"favColor" validate:"hexcolor|rgb|rgba"` - Addresses []*Address `json:"addresses" validate:"required,dive,required"` // a User can have a home and cottage... -} - -// Address houses a users address information. -type Address struct { - Street string `json:"street" validate:"required"` - City string `json:"city" validate:"required"` - Planet string `json:"planet" validate:"required"` - Phone string `json:"phone" validate:"required"` -} - -type validationError struct { - ActualTag string `json:"tag"` - Namespace string `json:"namespace"` - Kind string `json:"kind"` - Type string `json:"type"` - Value string `json:"value"` - Param string `json:"param"` -} - -func wrapValidationErrors(errs validator.ValidationErrors) []validationError { - validationErrors := make([]validationError, 0, len(errs)) - for _, validationErr := range errs { - validationErrors = append(validationErrors, validationError{ - ActualTag: validationErr.ActualTag(), - Namespace: validationErr.Namespace(), - Kind: validationErr.Kind().String(), - Type: validationErr.Type().String(), - Value: fmt.Sprintf("%v", validationErr.Value()), - Param: validationErr.Param(), - }) - } - - return validationErrors -} - -func postUser(ctx iris.Context) { - var user User - err := ctx.ReadJSON(&user) - if err != nil { - // Handle the error, below you will find the right way to do that... - - if errs, ok := err.(validator.ValidationErrors); ok { - // Wrap the errors with JSON format, the underline library returns the errors as interface. - validationErrors := wrapValidationErrors(errs) - - // Fire an application/json+problem response and stop the handlers chain. - ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem(). - Title("Validation error"). - Detail("One or more fields failed to be validated"). - Type("/user/validation-errors"). - Key("errors", validationErrors)) - - return - } - - // It's probably an internal JSON error, let's dont give more info here. - ctx.StopWithStatus(iris.StatusInternalServerError) - return - } - - ctx.JSON(iris.Map{"message": "OK"}) -} - -func resolveErrorsDocumentation(ctx iris.Context) { - ctx.WriteString("A page that should document to web developers or users of the API on how to resolve the validation errors") -} diff --git a/_examples/request-body/read-json/main.go b/_examples/request-body/read-json/main.go deleted file mode 100644 index fee580f5bf..0000000000 --- a/_examples/request-body/read-json/main.go +++ /dev/null @@ -1,62 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -type Company struct { - Name string - City string - Other string -} - -func MyHandler(ctx iris.Context) { - var c Company - - if err := ctx.ReadJSON(&c); err != nil { - ctx.StopWithError(iris.StatusBadRequest, err) - return - } - - ctx.Writef("Received: %#+v\n", c) -} - -// simple json stuff, read more at https://golang.org/pkg/encoding/json -type Person struct { - Name string `json:"name"` - Age int `json:"age"` -} - -// MyHandler2 reads a collection of Person from JSON post body. -func MyHandler2(ctx iris.Context) { - var persons []Person - err := ctx.ReadJSON(&persons) - if err != nil { - ctx.StopWithError(iris.StatusBadRequest, err) - return - } - - ctx.Writef("Received: %#+v\n", persons) -} - -func main() { - app := iris.New() - - app.Post("/", MyHandler) - app.Post("/slice", MyHandler2) - - // use Postman or whatever to do a POST request - // to the http://localhost:8080 with RAW BODY: - /* - { - "Name": "iris-Go", - "City": "New York", - "Other": "Something here" - } - */ - // and Content-Type to application/json (optionally but good practise) - // - // The response should be: - // Received: main.Company{Name:"iris-Go", City:"New York", Other:"Something here"} - app.Listen(":8080", iris.WithOptimizations) -} diff --git a/_examples/request-body/read-many/main.go b/_examples/request-body/read-many/main.go deleted file mode 100644 index cc22d775c0..0000000000 --- a/_examples/request-body/read-many/main.go +++ /dev/null @@ -1,60 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.Post("/", logAllBody, logJSON, logFormValues, func(ctx iris.Context) { - // body, err := ioutil.ReadAll(ctx.Request().Body) once or - body, err := ctx.GetBody() // as many times as you need. - if err != nil { - ctx.StopWithError(iris.StatusInternalServerError, err) - return - } - - if len(body) == 0 { - ctx.WriteString(`The body was empty -or iris.WithoutBodyConsumptionOnUnmarshal option is missing from app.Run. -Check the terminal window for any queries logs.`) - } else { - ctx.WriteString("OK body is still:\n") - ctx.Write(body) - } - }) - - // With ctx.UnmarshalBody, ctx.ReadJSON, ctx.ReadXML, ctx.ReadForm, ctx.FormValues - // and ctx.GetBody methods the default golang and net/http behavior - // is to consume the readen data - they are not available on any next handlers in the chain - - // to change that behavior just pass the `WithoutBodyConsumptionOnUnmarshal` option. - app.Listen(":8080", iris.WithoutBodyConsumptionOnUnmarshal) -} - -func logAllBody(ctx iris.Context) { - body, err := ctx.GetBody() - if err == nil && len(body) > 0 { - ctx.Application().Logger().Infof("logAllBody: %s", string(body)) - } - - ctx.Next() -} - -func logJSON(ctx iris.Context) { - var p interface{} - if err := ctx.ReadJSON(&p); err == nil { - ctx.Application().Logger().Infof("logJSON: %#+v", p) - } - - ctx.Next() -} - -func logFormValues(ctx iris.Context) { - values := ctx.FormValues() - if values != nil { - ctx.Application().Logger().Infof("logFormValues: %v", values) - } - - ctx.Next() -} diff --git a/_examples/request-body/read-msgpack/main.go b/_examples/request-body/read-msgpack/main.go deleted file mode 100644 index 4a4e92b5bc..0000000000 --- a/_examples/request-body/read-msgpack/main.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -// User example struct to bind to. -type User struct { - Firstname string `msgpack:"firstname"` - Lastname string `msgpack:"lastname"` - City string `msgpack:"city"` - Age int `msgpack:"age"` -} - -// readMsgPack reads a `User` from MsgPack post body. -func readMsgPack(ctx iris.Context) { - var u User - err := ctx.ReadMsgPack(&u) - if err != nil { - ctx.StopWithError(iris.StatusBadRequest, err) - return - } - - ctx.Writef("Received: %#+v\n", u) -} - -func main() { - app := iris.New() - app.Post("/", readMsgPack) - - // POST: http://localhost:8080 - // - // To run the example, use a tool like Postman: - // 1. Body: Binary - // 2. Select File, select the one from "_examples/response-writer/write-rest" example. - // The output should be: - // Received: main.User{Firstname:"John", Lastname:"Doe", City:"Neither FBI knows!!!", Age:25} - app.Listen(":8080") -} diff --git a/_examples/request-body/read-query/main.go b/_examples/request-body/read-query/main.go deleted file mode 100644 index ca0975ddb0..0000000000 --- a/_examples/request-body/read-query/main.go +++ /dev/null @@ -1,30 +0,0 @@ -// package main contains an example on how to use the ReadQuery, -// same way you can do the ReadJSON & ReadProtobuf and e.t.c. -package main - -import ( - "github.com/kataras/iris/v12" -) - -type MyType struct { - Name string `url:"name"` - Age int `url:"age"` -} - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - var t MyType - err := ctx.ReadQuery(&t) - if err != nil && !iris.IsErrPath(err) { - ctx.StopWithError(iris.StatusInternalServerError, err) - return - } - - ctx.Writef("MyType: %#v", t) - }) - - // http://localhost:8080?name=iris&age=3 - app.Listen(":8080") -} diff --git a/_examples/request-body/read-xml/main.go b/_examples/request-body/read-xml/main.go deleted file mode 100644 index 9b42e9aea1..0000000000 --- a/_examples/request-body/read-xml/main.go +++ /dev/null @@ -1,49 +0,0 @@ -package main - -import ( - "encoding/xml" - - "github.com/kataras/iris/v12" -) - -func main() { - app := newApp() - - // use Postman or whatever to do a POST request - // to the http://localhost:8080 with RAW BODY: - /* - - Description of this person, the body of this inner element. - - */ - // and Content-Type to application/xml (optionally but good practise) - // - // The response should be: - // Received: main.person{XMLName:xml.Name{Space:"", Local:"person"}, Name:"Winston Churchill", Age:90, Description:"Description of this person, the body of this inner element."} - app.Listen(":8080", iris.WithOptimizations) -} - -func newApp() *iris.Application { - app := iris.New() - app.Post("/", handler) - - return app -} - -// simple xml stuff, read more at https://golang.org/pkg/encoding/xml -type person struct { - XMLName xml.Name `xml:"person"` // element name - Name string `xml:"name,attr"` // ,attr for attribute. - Age int `xml:"age,attr"` // ,attr attribute. - Description string `xml:"description"` // inner element name, value is its body. -} - -func handler(ctx iris.Context) { - var p person - if err := ctx.ReadXML(&p); err != nil { - ctx.StopWithError(iris.StatusBadRequest, err) - return - } - - ctx.Writef("Received: %#+v", p) -} diff --git a/_examples/request-body/read-xml/main_test.go b/_examples/request-body/read-xml/main_test.go deleted file mode 100644 index 3fa36b315d..0000000000 --- a/_examples/request-body/read-xml/main_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestReadXML(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - expectedResponse := `Received: main.person{XMLName:xml.Name{Space:"", Local:"person"}, Name:"Winston Churchill", Age:90, Description:"Description of this person, the body of this inner element."}` - send := `Description of this person, the body of this inner element.` - - e.POST("/").WithText(send).Expect(). - Status(httptest.StatusOK).Body().Equal(expectedResponse) -} diff --git a/_examples/request-body/read-yaml/main.go b/_examples/request-body/read-yaml/main.go deleted file mode 100644 index b353e94c02..0000000000 --- a/_examples/request-body/read-yaml/main.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func newApp() *iris.Application { - app := iris.New() - app.Post("/", handler) - - return app -} - -// simple yaml stuff, read more at https://yaml.org/start.html -type product struct { - Invoice int `yaml:"invoice"` - Tax float32 `yaml:"tax"` - Total float32 `yaml:"total"` - Comments string `yaml:"comments"` -} - -func handler(ctx iris.Context) { - var p product - if err := ctx.ReadYAML(&p); err != nil { - ctx.StopWithError(iris.StatusBadRequest, err) - return - } - - ctx.Writef("Received: %#+v", p) -} - -func main() { - app := newApp() - app.Listen(":8080") -} diff --git a/_examples/request-body/read-yaml/main_test.go b/_examples/request-body/read-yaml/main_test.go deleted file mode 100644 index 644ba55897..0000000000 --- a/_examples/request-body/read-yaml/main_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestReadYAML(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - expectedResponse := `Received: main.product{Invoice:34843, Tax:251.42, Total:4443.52, Comments:"Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338."}` - send := `invoice: 34843 -tax : 251.42 -total: 4443.52 -comments: > - Late afternoon is best. - Backup contact is Nancy - Billsmer @ 338-4338.` - - e.POST("/").WithHeader("Content-Type", "application/x-yaml").WithBytes([]byte(send)).Expect(). - Status(httptest.StatusOK).Body().Equal(expectedResponse) -} diff --git a/_examples/request-ratelimit/main.go b/_examples/request-ratelimit/main.go deleted file mode 100644 index 8462952482..0000000000 --- a/_examples/request-ratelimit/main.go +++ /dev/null @@ -1,90 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/rate" -) - -func main() { - app := newApp() - app.Logger().SetLevel("debug") - - // * http://localhost:8080/v1 - // * http://localhost:8080/v1/other - // * http://localhost:8080/v2/list (with X-API-Key request header) - // Read more at: https://en.wikipedia.org/wiki/Token_bucket - // - // Alternatives: - // * https://github.com/iris-contrib/middleware/blob/master/throttler/_example/main.go - // Read more at: https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm - // * https://github.com/iris-contrib/middleware/tree/master/tollboothic/_examples/limit-handler - app.Listen(":8080") -} - -func newApp() *iris.Application { - app := iris.New() - - v1 := app.Party("/v1") - { - // Register the rate limiter middleware at the "/v1" subrouter. - // - // Fist and second input parameters: - // Allow 1 request per second, with a maximum burst size of 5. - // - // Third optional variadic input parameter: - // Can be a cleanup function. - // Iris provides a cleanup function that will check for old entries and remove them. - // You can customize it, e.g. check every 1 minute - // if a client's last visit was 5 minutes ago ("old" entry) - // and remove it from the memory. - limitV1 := rate.Limit(1, 5, rate.PurgeEvery(time.Minute, 5*time.Minute)) - // rate.Every helper: - // rate.Limit(rate.Every(time.Minute), 5) - v1.Use(limitV1) - - v1.Get("/", index) - v1.Get("/other", other) - } - - v2 := app.Party("/v2") - { - v2.Use(useAPIKey) - // Initialize a new rate limit middleware to limit requests - // per API Key(see `useAPIKey` below) instead of client's Remote IP Address. - limitV2 := rate.Limit(rate.Every(time.Minute), 300, rate.PurgeEvery(5*time.Minute, 15*time.Minute)) - v2.Use(limitV2) - - v2.Get("/list", list) - } - - return app -} - -func useAPIKey(ctx iris.Context) { - apiKey := ctx.GetHeader("X-API-Key") - if apiKey == "" { // [validate your API Key here...] - ctx.StopWithStatus(iris.StatusForbidden) - return - } - - // Change the method that rate limit matches the requests with a specific user - // and set our own api key as theirs identifier. - rate.SetIdentifier(ctx, apiKey) - ctx.Next() -} - -func list(ctx iris.Context) { - ctx.JSON(iris.Map{"key": "value"}) -} - -func index(ctx iris.Context) { - ctx.HTML("

Index Page

") -} - -func other(ctx iris.Context) { - ctx.HTML("

Other Page

") -} - -// Note: Use `ctx.SendFileWithRate` to use a download rate limiter instead. diff --git a/_examples/request-referrer/main.go b/_examples/request-referrer/main.go deleted file mode 100644 index adfbe3b7ff..0000000000 --- a/_examples/request-referrer/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - // GetReferrer extracts and returns the information from the "Referer" (or "Referrer") header - // and url query parameter as specified in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy. - r := ctx.GetReferrer() - switch r.Type { - case iris.ReferrerSearch: - ctx.Writef("Search %s: %s\n", r.Label, r.Query) - ctx.Writef("Google: %s\n", r.GoogleType) - case iris.ReferrerSocial: - ctx.Writef("Social %s\n", r.Label) - case iris.ReferrerIndirect: - ctx.Writef("Indirect: %s\n", r.URL) - } - }) - - // http://localhost:8080?referrer=https://twitter.com/Xinterio/status/1023566830974251008 - // http://localhost:8080?referrer=https://www.google.com/search?q=Top+6+golang+web+frameworks&oq=Top+6+golang+web+frameworks - app.Listen(":8080") -} diff --git a/_examples/response-writer/cache/client-side/main.go b/_examples/response-writer/cache/client-side/main.go deleted file mode 100644 index f460b4636d..0000000000 --- a/_examples/response-writer/cache/client-side/main.go +++ /dev/null @@ -1,39 +0,0 @@ -// Package main shows how you can use the `WriteWithExpiration` -// based on the "modtime", if it's newer than the request header then -// it will refresh the contents, otherwise will let the client (99.9% the browser) -// to handle the cache mechanism, it's faster than iris.Cache because server-side -// has nothing to do and no need to store the responses in the memory. -package main - -import ( - "time" - - "github.com/kataras/iris/v12" -) - -const refreshEvery = 10 * time.Second - -func main() { - app := iris.New() - app.Use(iris.Cache304(refreshEvery)) - // same as: - // app.Use(func(ctx iris.Context) { - // now := time.Now() - // if modified, err := ctx.CheckIfModifiedSince(now.Add(-refresh)); !modified && err == nil { - // ctx.WriteNotModified() - // return - // } - - // ctx.SetLastModified(now) - - // ctx.Next() - // }) - - app.Get("/", greet) - app.Listen(":8080") -} - -func greet(ctx iris.Context) { - ctx.Header("X-Custom", "my custom header") - ctx.Writef("Hello World! %s", time.Now()) -} diff --git a/_examples/response-writer/cache/simple/main.go b/_examples/response-writer/cache/simple/main.go deleted file mode 100644 index 010c351116..0000000000 --- a/_examples/response-writer/cache/simple/main.go +++ /dev/null @@ -1,80 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12" - - "github.com/kataras/iris/v12/cache" -) - -var markdownContents = []byte(`## Hello Markdown - -This is a sample of Markdown contents - - - -Features --------- - -All features of Sundown are supported, including: - -* **Compatibility**. The Markdown v1.0.3 test suite passes with - the --tidy option. Without --tidy, the differences are - mostly in whitespace and entity escaping, where blackfriday is - more consistent and cleaner. - -* **Common extensions**, including table support, fenced code - blocks, autolinks, strikethroughs, non-strict emphasis, etc. - -* **Safety**. Blackfriday is paranoid when parsing, making it safe - to feed untrusted user input without fear of bad things - happening. The test suite stress tests this and there are no - known inputs that make it crash. If you find one, please let me - know and send me the input that does it. - - NOTE: "safety" in this context means *runtime safety only*. In order to - protect yourself against JavaScript injection in untrusted content, see - [this example](https://github.com/russross/blackfriday#sanitize-untrusted-content). - -* **Fast processing**. It is fast enough to render on-demand in - most web applications without having to cache the output. - -* **Routine safety**. You can run multiple parsers in different - goroutines without ill effect. There is no dependence on global - shared state. - -* **Minimal dependencies**. Blackfriday only depends on standard - library packages in Go. The source code is pretty - self-contained, so it is easy to add to any project, including - Google App Engine projects. - -* **Standards compliant**. Output successfully validates using the - W3C validation tool for HTML 4.01 and XHTML 1.0 Transitional. - - [this is a link](https://github.com/kataras/iris) `) - -// Cache should not be used on handlers that contain dynamic data. -// Cache is a good and a must-feature on static content, i.e "about page" or for a whole blog site. -func main() { - app := iris.New() - app.Logger().SetLevel("debug") - app.Get("/", cache.Handler(10*time.Second), writeMarkdown) - // saves its content on the first request and serves it instead of re-calculating the content. - // After 10 seconds it will be cleared and reset. - - app.Listen(":8080") -} - -func writeMarkdown(ctx iris.Context) { - // tap multiple times the browser's refresh button and you will - // see this println only once every 10 seconds. - println("Handler executed. Content refreshed.") - - ctx.Markdown(markdownContents) -} - -/* Note that `HandleDir` does use the browser's disk caching by-default -therefore, register the cache handler AFTER any HandleDir calls, -for a faster solution that server doesn't need to keep track of the response -navigate to https://github.com/kataras/iris/blob/master/_examples/cache/client-side/main.go */ diff --git a/_examples/response-writer/content-negotiation/main.go b/_examples/response-writer/content-negotiation/main.go deleted file mode 100644 index d5619e1d2e..0000000000 --- a/_examples/response-writer/content-negotiation/main.go +++ /dev/null @@ -1,114 +0,0 @@ -// Package main contains three different ways to render content based on the client's accepted. -package main - -import "github.com/kataras/iris/v12" - -type testdata struct { - Name string `json:"name" xml:"Name"` - Age int `json:"age" xml:"Age"` -} - -func newApp() *iris.Application { - app := iris.New() - app.Logger().SetLevel("debug") - - // app.Use(func(ctx iris.Context) { - // requestedMime := ctx.URLParamDefault("type", "application/json") - // - // ctx.Negotiation().Accept.Override().MIME(requestedMime, nil) - // ctx.Next() - // }) - - app.Get("/resource", func(ctx iris.Context) { - data := testdata{ - Name: "test name", - Age: 26, - } - - // Server allows response only JSON and XML. These values - // are compared with the clients mime needs. Iris comes with default mime types responses - // but you can add a custom one by the `Negotiation().Mime(mime, content)` method, - // same for the "accept". - // You can also pass a custom ContentSelector(mime string) or ContentNegotiator to the - // `Context.Negotiate` method if you want to perform more advanced things. - // - // - // By-default the client accept mime is retrieved by the "Accept" header - // Indeed you can override or update it by `Negotiation().Accept.XXX` i.e - // ctx.Negotiation().Accept.Override().XML() - // - // All these values can change inside middlewares, the `Negotiation().Override()` and `.Accept.Override()` - // can override any previously set values. - // Order matters, if the client accepts anything (*/*) - // then the first prioritized mime's response data will be rendered. - ctx.Negotiation().JSON().XML() - // Accept-Charset vs: - ctx.Negotiation().Charset("utf-8", "iso-8859-7") - // Alternatively you can define the content/data per mime type - // anywhere in the handlers chain using the optional "v" variadic - // input argument of the Context.Negotiation().JSON,XML,YAML,Binary,Text,HTML(...) and e.t.c - // example (order matters): - // ctx.Negotiation().JSON(data).XML(data).Any("content for */*") - // ctx.Negotiate(nil) - - // if not nil passed in the `Context.Negotiate` method - // then it overrides any contents made by the negotitation builder above. - _, err := ctx.Negotiate(data) - if err != nil { - ctx.Writef("%v", err) - } - }) - - app.Get("/resource2", func(ctx iris.Context) { - jsonAndXML := testdata{ - Name: "test name", - Age: 26, - } - - // I prefer that one, as it gives me the freedom to modify - // response data per accepted mime content type on middlewares as well. - ctx.Negotiation(). - JSON(jsonAndXML). - XML(jsonAndXML). - HTML("

Test Name

Age 26

") - - ctx.Negotiate(nil) - }) - - app.Get("/resource3", func(ctx iris.Context) { - // If that line is missing and the requested - // mime type of content is */* or application/xml or application/json - // then 406 Not Acceptable http error code will be rendered instead. - // - // We also add the "gzip" algorithm as an option to encode - // resources on send. - ctx.Negotiation().JSON().XML().HTML().EncodingGzip() - - jsonAndXML := testdata{ - Name: "test name", - Age: 26, - } - - // Prefer that way instead of the '/resource2' above - // if "iris.N" is a static one and can be declared - // outside of a handler. - ctx.Negotiate(iris.N{ - // Text: for text/plain, - // Markdown: for text/mardown, - // Binary: for application/octet-stream, - // YAML: for application/x-yaml, - // JSONP: for text/javascript - // Other: for anything else, - JSON: jsonAndXML, // for application/json - XML: jsonAndXML, // for application/xml or text/xml - HTML: "

Test Name

Age 26

", // for text/html - }) - }) - - return app -} - -func main() { - app := newApp() - app.Listen(":8080") -} diff --git a/_examples/response-writer/content-negotiation/main_test.go b/_examples/response-writer/content-negotiation/main_test.go deleted file mode 100644 index 08c5e5a663..0000000000 --- a/_examples/response-writer/content-negotiation/main_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package main - -import ( - "bytes" - "compress/gzip" - "encoding/xml" - "io/ioutil" - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/httptest" -) - -func TestContentNegotiation(t *testing.T) { - var ( - expectedJSONResponse = testdata{ - Name: "test name", - Age: 26, - } - expectedXMLResponse, _ = xml.Marshal(expectedJSONResponse) - expectedHTMLResponse = "

Test Name

Age 26

" - ) - - app := newApp() - app.Configure(iris.WithOptimizations) - e := httptest.New(t, app) - - e.GET("/resource").WithHeader("Accept", "application/json"). - Expect().Status(httptest.StatusOK). - ContentType("application/json", "utf-8"). - JSON().Equal(expectedJSONResponse) - e.GET("/resource").WithHeader("Accept", "application/xml").WithHeader("Accept-Charset", "iso-8859-7"). - Expect().Status(httptest.StatusOK). - ContentType("application/xml", "iso-8859-7"). - Body().Equal(string(expectedXMLResponse)) - - e.GET("/resource2").WithHeader("Accept", "application/json"). - Expect().Status(httptest.StatusOK). - ContentType("application/json", "utf-8"). - JSON().Equal(expectedJSONResponse) - e.GET("/resource2").WithHeader("Accept", "application/xml"). - Expect().Status(httptest.StatusOK). - ContentType("application/xml", "utf-8"). - Body().Equal(string(expectedXMLResponse)) - e.GET("/resource2").WithHeader("Accept", "text/html"). - Expect().Status(httptest.StatusOK). - ContentType("text/html", "utf-8"). - Body().Equal(expectedHTMLResponse) - - e.GET("/resource3").WithHeader("Accept", "application/json"). - Expect().Status(httptest.StatusOK). - ContentType("application/json", "utf-8"). - JSON().Equal(expectedJSONResponse) - e.GET("/resource3").WithHeader("Accept", "application/xml"). - Expect().Status(httptest.StatusOK). - ContentType("application/xml", "utf-8"). - Body().Equal(string(expectedXMLResponse)) - - // test html with "gzip" encoding algorithm. - rawGzipResponse := e.GET("/resource3").WithHeader("Accept", "text/html"). - WithHeader("Accept-Encoding", "gzip"). - Expect().Status(httptest.StatusOK). - ContentType("text/html", "utf-8"). - ContentEncoding("gzip"). - Body().Raw() - - zr, err := gzip.NewReader(bytes.NewReader([]byte(rawGzipResponse))) - if err != nil { - t.Fatal(err) - } - - rawResponse, err := ioutil.ReadAll(zr) - if err != nil { - t.Fatal(err) - } - - if expected, got := expectedHTMLResponse, string(rawResponse); expected != got { - t.Fatalf("expected response to be:\n%s but got:\n%s", expected, got) - } -} diff --git a/_examples/response-writer/http2push/main.go b/_examples/response-writer/http2push/main.go deleted file mode 100644 index f6b9f4d952..0000000000 --- a/_examples/response-writer/http2push/main.go +++ /dev/null @@ -1,47 +0,0 @@ -// Server push lets the server preemptively "push" website assets -// to the client without the user having explicitly asked for them. -// When used with care, we can send what we know the user is going -// to need for the page they're requesting. -package main - -import ( - "net/http" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - app.Get("/", pushHandler) - app.Get("/main.js", simpleAssetHandler) - - app.Run(iris.TLS("127.0.0.1:443", "mycert.crt", "mykey.key")) - // $ openssl req -new -newkey rsa:4096 -x509 -sha256 \ - // -days 365 -nodes -out mycert.crt -keyout mykey.key -} - -func pushHandler(ctx iris.Context) { - // The target must either be an absolute path (like "/path") or an absolute - // URL that contains a valid host and the same scheme as the parent request. - // If the target is a path, it will inherit the scheme and host of the - // parent request. - target := "/main.js" - - if pusher, ok := ctx.ResponseWriter().Naive().(http.Pusher); ok { - err := pusher.Push(target, nil) - if err != nil { - if err == iris.ErrPushNotSupported { - ctx.StopWithText(iris.StatusHTTPVersionNotSupported, "HTTP/2 push not supported.") - } else { - ctx.StopWithError(iris.StatusInternalServerError, err) - } - return - } - } - - ctx.HTML(``, target) -} - -func simpleAssetHandler(ctx iris.Context) { - ctx.ServeFile("./public/main.js") -} diff --git a/_examples/response-writer/http2push/mycert.crt b/_examples/response-writer/http2push/mycert.crt deleted file mode 100644 index 9db93b09df..0000000000 --- a/_examples/response-writer/http2push/mycert.crt +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFazCCA1OgAwIBAgIUfwMd9auWixp19UnXOmyxJ9Jkv7IwDQYJKoZIhvcNAQEL -BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM -GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA2MjUwOTUxNDdaFw0yMTA2 -MjUwOTUxNDdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw -HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQDlVGyGAQ9uyfNbwZyrtYOSjLpxf5NpNToh2OzU7gy2 -OexBji5lmWBQ3oYDG+FjAkbHORPzOMNpeMwje+IjGZBw8x6E+8WoGdSzbrEZ6pUV -wKJGKEuDlx6g6HEmtv3ZwgGe20gvPjjW+oCO888dwK/mbIHrHTq4nO3o0gAdAJwu -amn9BlHU5O4RW7BQ4tLF+j/fBCACWRG1NHXA0AT8eg544GyCdyteAH11oCDsHS8/ -DAPsM6t+tZrMCIt9+9dzPdVoOmQNaMMrcz8eJohddRTK6zHe9ixZTt/soayOF7OS -QQeekbr3HPYhD450zRVplLMHx7wnph/+O+Po6bqDnUzdnkqAAwwymQapHMuHXZKN -rhdfKau3rVo1GeXLIRgeWLUoxFSm4TYshrgt+0AidLRH+dCY7MS9Ngga/sAK3vID -gSF75mFgOhY+q7nvY9Ecao6TnoNNRY29hUat4y0VwSyysUy887vHr6lMK5CrAT/l -Ch8fuu20HUCoiLwMJvA6+wpivZkuiIvWY7bVGYsEYrrW+bCNN9wCGYTZEyX++os9 -v/38wdOqGUT00ewXkjIUFCWbrnxxSr98kF3w3wPf9K4Y40MNxeR90nyX4zjXGF1/ -91msUh+iivsz9mcN9DK83fgTyOsoVLX5cm/L2UBwMacsfjBbN4djOc5IuYMar/VN -GQIDAQABo1MwUTAdBgNVHQ4EFgQUtkf+yAvqgZC8f22iJny9hFEDolMwHwYDVR0j -BBgwFoAUtkf+yAvqgZC8f22iJny9hFEDolMwDwYDVR0TAQH/BAUwAwEB/zANBgkq -hkiG9w0BAQsFAAOCAgEAE2QasBVru618rxupyJgEHw6r4iv7sz1Afz3Q5qJ4oSA9 -xVsrVCjr3iHRFSw8Rf670E8Ffk/JjzS65mHw6zeZj/ANBKQWLjRlqzYXeetq5HzG -SIgaG7p1RFvvzz3+leFGzjinZ6sKbfB4OB72o2YN+fO8DsDxgGKll0W4KAazizSe -HY9Pgu437tWnwF16rFO3IL47n5HzYlRoGIPOpzFoNX5+fyn9GlnKEtONF2QBKTjY -rdjvqFRByDiC74d8z/Yx8IiDRn1mTcG90JLR9+c6M7fruha9Y/rJfw+4AhVh5ZDz -Bl9rGPjwEs5zwutYvVAJzs7AVcighYP1lHKoJ7DxBDQeyBsYlUNk2l6bmZgLgGUZ -+2OyWlqc/jD2GdDsIaZ4i7QqhTI/6aYZIf5zUkblKV1aMSaDulKxRv//OwW28Jax -9EEoV7VaFb3sOkB/tZGhusXeQVtdrhahT3KkZLNwmNXoXWKJ5LjeUlFWJyV6JbDe -y/PIWWCwWqyuFCSZS+Cg3RDgAzfSxkI8uVZ+IKKJS3UluDX45lxXtbRrvTQ+oDrA -6ga5c1Vz9C4kn1K5yW4d7QIvg6vPiy7gvl+//sz9oxUM3yswInDBY0HKLgT0Uq9b -YzLDh2RSaHsgHMPy2BKqR+q2N+lpg7inAWuJM1Huq6eHFqhiyQkzsfscBd1Dpm8= ------END CERTIFICATE----- diff --git a/_examples/response-writer/http2push/mykey.key b/_examples/response-writer/http2push/mykey.key deleted file mode 100644 index 39f7b3af50..0000000000 --- a/_examples/response-writer/http2push/mykey.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDlVGyGAQ9uyfNb -wZyrtYOSjLpxf5NpNToh2OzU7gy2OexBji5lmWBQ3oYDG+FjAkbHORPzOMNpeMwj -e+IjGZBw8x6E+8WoGdSzbrEZ6pUVwKJGKEuDlx6g6HEmtv3ZwgGe20gvPjjW+oCO -888dwK/mbIHrHTq4nO3o0gAdAJwuamn9BlHU5O4RW7BQ4tLF+j/fBCACWRG1NHXA -0AT8eg544GyCdyteAH11oCDsHS8/DAPsM6t+tZrMCIt9+9dzPdVoOmQNaMMrcz8e -JohddRTK6zHe9ixZTt/soayOF7OSQQeekbr3HPYhD450zRVplLMHx7wnph/+O+Po -6bqDnUzdnkqAAwwymQapHMuHXZKNrhdfKau3rVo1GeXLIRgeWLUoxFSm4TYshrgt -+0AidLRH+dCY7MS9Ngga/sAK3vIDgSF75mFgOhY+q7nvY9Ecao6TnoNNRY29hUat -4y0VwSyysUy887vHr6lMK5CrAT/lCh8fuu20HUCoiLwMJvA6+wpivZkuiIvWY7bV -GYsEYrrW+bCNN9wCGYTZEyX++os9v/38wdOqGUT00ewXkjIUFCWbrnxxSr98kF3w -3wPf9K4Y40MNxeR90nyX4zjXGF1/91msUh+iivsz9mcN9DK83fgTyOsoVLX5cm/L -2UBwMacsfjBbN4djOc5IuYMar/VNGQIDAQABAoICAQCtWx1SSxjkcerxsLEDKApW -zOTfiUXgoOjZz0ZwS6b2VWDfyWAPU1r4ps39KaU+F+lzDhWjpYQqhbMjG7G9QMTs -bQvkEQLAaQ5duU5NPgQG1oCUsj8rMSBpGGz4jBnm834QHMk7VTjYYbKu3WTyo8cU -U2/+UDEkfxRlC+IkCmMFv1FxgMZ5PbktC/eDnYMhP2Pq7Q5ZWAVHymk9IMK0LHwm -Kdg842K4A3zTXwGkGwetDCMm+YQpG5TxqX/w82BRcCuTR5h8fnYSsWLEIvKwWyIl -ppcjaUnrFPG2yhxLqWUIKPpehuEjjhQMt9rDNoh6MHsJZZY5Dp5eq91EIvLoLQ99 -hXBmD4P8LDop4r0jniPZJi/ACsaD0jBooA4525+Kouq7RP28Jp/pek7lVOOcBgRv -D3zyESbKfqoaOfyfQ2ff4sILnTAr4V2nq3ekphGEYJrWN0ZoADcLdnr1cZ8L+VBI -o/4mi5/3HID/UEDliHSa97hxxGBEqTto0ZuXuNwfwx5ho33uVT6zNwRgiJ62Bgu3 -Fhk/wVGuZxWvb1KHUNInG9cvsslhO4Vu9wJvYj91BnRq36rsyKKid5DrU+PNgmog -lw3IXQpTojyRCYPuG9TKqEZ6b+so7GTKhBOjiwaupMOletVRGSAdbE81VN6HtxNW -aj39+FnxzMAlsieib+PBAQKCAQEA+t1fOYSaZBo7pZUmo2S0zulUEJjrYRGKJlWJ -4psWSwFu/7/3UL4q0RBQaSRew9u/YSpaNlBYfcpnFVOjiLwHq5Hx46Eq0BuKsNlJ -1/qxw9qjHqcrOre6K4/7NaWLPuM9fEmV+3MhFVXgv+WC5BHOowRTlOG30vIcC1J2 -L5xsBUsxDDY13cD1bLKRmFcyMFM8y7wMZmo7H/WfVmyoPKQaC43pTcmIXH0Jr2Ws -Wsfh18mhjtamaOPEFx5K0x4d0PI8tW5ouiUUkVIDaue27XfS969qEChv768/44eX -WeqcekaG9jv2noMClt79rYd3Lne9HkgY6IT9FT+JqXfu+KYwuQKCAQEA6gYzUsGB -9GQO8DE8AYn7JwNOtg1X4zKakXiGxH+nuZb7wJjAeGdYqTHySxPBXg0A2nDwoyz5 -4sAdLAr3FZoIvTzo7M5KIKFDzfyDmQDavhroH1mBAEiqKGNniP+RND3nWBBqDK1R -qcqbhI3Kj5Ycany6a4nP+hZRBIyT9sfJ0S0YruSY8IGXgDwhlJrZ7bsWMZylrgD/ -1qnPL0KqVBY8YR8msRj88h72IlD5o0kwvisOIvyhA0YgwGBb6lg7A+DifiF03ZlS -2yELbIkKDVr+p3jC7MBh4B+OJY68AMl6wVjAaDM1AZnpjKE5YmZg5+Ks5823zILo -PrSB9hn0+DIPYQKCAQEAh9x+JuNmzhHa/dkiHNl8hpadHYQD7gUWwZ4P1/bQAv0a -xU2MvmDPRXxFYDv/SqlnI1NRmhq3YiDM5SLv7SyQJt4al4IAcsaHvTFgqaSuw3hU -YVR9uAYqwE7w6OPn3r4o3Xfoz05Ru4FP//1nfucZ9vVv4rC/4nGWuJcHRM+9PLy1 -KnztfVR0VlL7QPrwRnW99kS4nnqn3K4khiTAlF73cAyCLsuXmydoqGIzDtMzv68G -XRpo82NvHmoccevcj/2w3T2XYECWvAEjsrEdQ8xiKBwLIAcWYEOUIUCcumiyKBKs -IwzkioI/U8AeuO0lobfdZ1n6i2sCuZA4mNxIQseWmQKCAQEA5YkfXdQeuq5JWJ1x -1bCYfjNoSHfd9CH2KSimRqVOxWGpm8Y3QeFbvNgYZjsCNlVauOZ9oA7FKfp0onY+ -0xk56SKM83eCjW6fKrK6AKAt7LhHZDhNpxGek+6r5luE+FCfUGkJG1YD+x2WW/UW -8K6zQF8GGeQZ8Zlh7axUlIBxGpG43BGrUHpLNqPD7BXWGq6dnhufBYRFay8y34/r -sH3+yuPa92ki7/geQppZwCZRgLSKMRbIdoWaKhZZEQlpGOzCOiRmk9OGyRcoNVRU -X7UYgPqZdc1cMo/AxGWzULJNjMaYMZvIKcHkqOKZfkIcWlSictn7pMPhN1+k+NWM -yMORAQKCAQAyXl02h/c2ihx6cjKlnNeDr2ZfzkoiAvFuKaoAR+KVvb9F9X7ZgKSi -wudZyelTglIVCYXeRmG09uX3rNGCzFrweRwgn6x/8DnN5pMRJVZOXFdgR+V9uKep -K6F7DYbPyggvLOAsezB+09i9lwxM+XdA2whVpL5NFR1rGfFglnE1EQHcEvNONkcv -0h8x9cNSptJyRDLiTIKI9EhonuzwzkGpvjULQE8MLbT8PbjoLFINcE9ZWhwtyw0V -XO32KE8iLKt3KzHz9CfTRCI3M7DwD752AC6zRr8ZS/HXzs+5WTkdVVEtRC7Abd3y -W2TzuSMYNDu876twbTVQJED3mwOAQ3J7 ------END PRIVATE KEY----- diff --git a/_examples/response-writer/http2push/public/main.js b/_examples/response-writer/http2push/public/main.js deleted file mode 100644 index 2aae6c4cc8..0000000000 --- a/_examples/response-writer/http2push/public/main.js +++ /dev/null @@ -1 +0,0 @@ -window.alert("javascript loaded"); \ No newline at end of file diff --git a/_examples/response-writer/protobuf/README.md b/_examples/response-writer/protobuf/README.md deleted file mode 100644 index 4c69034cb2..0000000000 --- a/_examples/response-writer/protobuf/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Protocol Buffers - -The `Context.Protobuf(proto.Message)` is the method which sends protos to the client. It accepts a [proto.Message](https://godoc.org/google.golang.org/protobuf/proto#Message) value. - -> Note: Iris is using the newest version of the Go protocol buffers implementation. Read more about it at [The Go Blog: A new Go API for Protocol Buffers](https://blog.golang.org/protobuf-apiv2). - - -1. Install the protoc-gen-go tool. - -```sh -$ go get -u google.golang.org/protobuf/cmd/protoc-gen-go@latest -``` - -2. Generate proto - -```sh -$ protoc -I protos/ protos/hello.proto --go_out=. -``` diff --git a/_examples/response-writer/protobuf/go.mod b/_examples/response-writer/protobuf/go.mod deleted file mode 100644 index 366f0b140c..0000000000 --- a/_examples/response-writer/protobuf/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module app - -go 1.15 diff --git a/_examples/response-writer/protobuf/main.go b/_examples/response-writer/protobuf/main.go deleted file mode 100644 index 8996c8131c..0000000000 --- a/_examples/response-writer/protobuf/main.go +++ /dev/null @@ -1,60 +0,0 @@ -package main - -import ( - "app/protos" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.Get("/", send) - app.Get("/json", sendAsJSON) - app.Post("/read", read) - app.Post("/read_json", readFromJSON) - - app.Listen(":8080") -} - -func send(ctx iris.Context) { - response := &protos.HelloReply{Message: "Hello, World!"} - ctx.Protobuf(response) -} - -func sendAsJSON(ctx iris.Context) { - response := &protos.HelloReply{Message: "Hello, World!"} - options := iris.JSON{ - Proto: iris.ProtoMarshalOptions{ - AllowPartial: true, - Multiline: true, - Indent: " ", - }, - } - - ctx.JSON(response, options) -} - -func read(ctx iris.Context) { - var request protos.HelloRequest - - err := ctx.ReadProtobuf(&request) - if err != nil { - ctx.StopWithError(iris.StatusBadRequest, err) - return - } - - ctx.Writef("HelloRequest.Name = %s", request.Name) -} - -func readFromJSON(ctx iris.Context) { - var request protos.HelloRequest - - err := ctx.ReadJSONProtobuf(&request) - if err != nil { - ctx.StopWithError(iris.StatusBadRequest, err) - return - } - - ctx.Writef("HelloRequest.Name = %s", request.Name) -} diff --git a/_examples/response-writer/protobuf/protos/hello.pb.go b/_examples/response-writer/protobuf/protos/hello.pb.go deleted file mode 100644 index ee512210d8..0000000000 --- a/_examples/response-writer/protobuf/protos/hello.pb.go +++ /dev/null @@ -1,209 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.25.0 -// protoc v3.11.1 -// source: hello.proto - -package protos - -import ( - proto "github.com/golang/protobuf/proto" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - -type HelloRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` -} - -func (x *HelloRequest) Reset() { - *x = HelloRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_hello_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *HelloRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HelloRequest) ProtoMessage() {} - -func (x *HelloRequest) ProtoReflect() protoreflect.Message { - mi := &file_hello_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. -func (*HelloRequest) Descriptor() ([]byte, []int) { - return file_hello_proto_rawDescGZIP(), []int{0} -} - -func (x *HelloRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type HelloReply struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` -} - -func (x *HelloReply) Reset() { - *x = HelloReply{} - if protoimpl.UnsafeEnabled { - mi := &file_hello_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *HelloReply) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HelloReply) ProtoMessage() {} - -func (x *HelloReply) ProtoReflect() protoreflect.Message { - mi := &file_hello_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HelloReply.ProtoReflect.Descriptor instead. -func (*HelloReply) Descriptor() ([]byte, []int) { - return file_hello_proto_rawDescGZIP(), []int{1} -} - -func (x *HelloReply) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -var File_hello_proto protoreflect.FileDescriptor - -var file_hello_proto_rawDesc = []byte{ - 0x0a, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, - 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_hello_proto_rawDescOnce sync.Once - file_hello_proto_rawDescData = file_hello_proto_rawDesc -) - -func file_hello_proto_rawDescGZIP() []byte { - file_hello_proto_rawDescOnce.Do(func() { - file_hello_proto_rawDescData = protoimpl.X.CompressGZIP(file_hello_proto_rawDescData) - }) - return file_hello_proto_rawDescData -} - -var file_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_hello_proto_goTypes = []interface{}{ - (*HelloRequest)(nil), // 0: protos.HelloRequest - (*HelloReply)(nil), // 1: protos.HelloReply -} -var file_hello_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_hello_proto_init() } -func file_hello_proto_init() { - if File_hello_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_hello_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HelloRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_hello_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HelloReply); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_hello_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_hello_proto_goTypes, - DependencyIndexes: file_hello_proto_depIdxs, - MessageInfos: file_hello_proto_msgTypes, - }.Build() - File_hello_proto = out.File - file_hello_proto_rawDesc = nil - file_hello_proto_goTypes = nil - file_hello_proto_depIdxs = nil -} diff --git a/_examples/response-writer/protobuf/protos/hello.proto b/_examples/response-writer/protobuf/protos/hello.proto deleted file mode 100644 index 312816151f..0000000000 --- a/_examples/response-writer/protobuf/protos/hello.proto +++ /dev/null @@ -1,13 +0,0 @@ -syntax = "proto3"; - -package protos; - -option go_package = "./protos"; - -message HelloRequest { - string name = 1; -} - -message HelloReply { - string message = 1; -} \ No newline at end of file diff --git a/_examples/response-writer/sse-third-party/main.go b/_examples/response-writer/sse-third-party/main.go deleted file mode 100644 index ed87b9f012..0000000000 --- a/_examples/response-writer/sse-third-party/main.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12" - "github.com/r3labs/sse" -) - -// First of all install the sse third-party package (you can use other if you don't like this approach or go ahead to the "sse" example) -// $ go get -u github.com/r3labs/sse -func main() { - app := iris.New() - s := sse.New() - /* - This creates a new stream inside of the scheduler. - Seeing as there are no consumers, publishing a message - to this channel will do nothing. - Clients can connect to this stream once the iris handler is started - by specifying stream as a url parameter, like so: - http://localhost:8080/events?stream=messages - */ - s.CreateStream("messages") - - app.Any("/events", iris.FromStd(s.HTTPHandler)) - - go func() { - // You design when to send messages to the client, - // here we just wait 5 seconds to send the first message - // in order to give u time to open a browser window... - time.Sleep(5 * time.Second) - // Publish a payload to the stream. - s.Publish("messages", &sse.Event{ - Data: []byte("ping"), - }) - - time.Sleep(3 * time.Second) - s.Publish("messages", &sse.Event{ - Data: []byte("second message"), - }) - time.Sleep(2 * time.Second) - s.Publish("messages", &sse.Event{ - Data: []byte("third message"), - }) - }() // ... - - app.Listen(":8080") -} - -/* For a golang SSE client you can look at: https://github.com/r3labs/sse#example-client */ diff --git a/_examples/response-writer/sse/main.go b/_examples/response-writer/sse/main.go deleted file mode 100644 index 7ac0a21daf..0000000000 --- a/_examples/response-writer/sse/main.go +++ /dev/null @@ -1,191 +0,0 @@ -// Package main shows how to send continuous event messages to the clients through SSE via a broker. -// Read details at: https://www.w3schools.com/htmL/html5_serversentevents.asp and -// https://robots.thoughtbot.com/writing-a-server-sent-events-server-in-go -package main - -import ( - "encoding/json" - "fmt" - "time" - - "github.com/kataras/golog" - "github.com/kataras/iris/v12" -) - -// A Broker holds open client connections, -// listens for incoming events on its Notifier channel -// and broadcast event data to all registered connections. -type Broker struct { - - // Events are pushed to this channel by the main events-gathering routine. - Notifier chan []byte - - // New client connections. - newClients chan chan []byte - - // Closed client connections. - closingClients chan chan []byte - - // Client connections registry. - clients map[chan []byte]bool -} - -// NewBroker returns a new broker factory. -func NewBroker() *Broker { - b := &Broker{ - Notifier: make(chan []byte, 1), - newClients: make(chan chan []byte), - closingClients: make(chan chan []byte), - clients: make(map[chan []byte]bool), - } - - // Set it running - listening and broadcasting events. - go b.listen() - - return b -} - -// Listen on different channels and act accordingly. -func (b *Broker) listen() { - for { - select { - case s := <-b.newClients: - // A new client has connected. - // Register their message channel. - b.clients[s] = true - golog.Infof("Client added. %d registered clients", len(b.clients)) - - case s := <-b.closingClients: - // A client has dettached and we want to - // stop sending them messages. - delete(b.clients, s) - golog.Infof("Removed client. %d registered clients", len(b.clients)) - - case event := <-b.Notifier: - // We got a new event from the outside! - // Send event to all connected clients. - for clientMessageChan := range b.clients { - clientMessageChan <- event - } - } - } -} - -func (b *Broker) ServeHTTP(ctx iris.Context) { - // Make sure that the writer supports flushing. - - flusher, ok := ctx.ResponseWriter().Flusher() - if !ok { - ctx.StopWithText(iris.StatusHTTPVersionNotSupported, "Streaming unsupported!") - return - } - - // Set the headers related to event streaming, you can omit the "application/json" if you send plain text. - // If you develop a go client, you must have: "Accept" : "application/json, text/event-stream" header as well. - ctx.ContentType("application/json, text/event-stream") - ctx.Header("Cache-Control", "no-cache") - ctx.Header("Connection", "keep-alive") - // We also add a Cross-origin Resource Sharing header so browsers on different domains can still connect. - ctx.Header("Access-Control-Allow-Origin", "*") - - // Each connection registers its own message channel with the Broker's connections registry. - messageChan := make(chan []byte) - - // Signal the broker that we have a new connection. - b.newClients <- messageChan - - // Listen to connection close and when the entire request handler chain exits(this handler here) and un-register messageChan. - ctx.OnClose(func(iris.Context) { - // Remove this client from the map of connected clients - // when this handler exits. - b.closingClients <- messageChan - }) - - // Block waiting for messages broadcast on this connection's messageChan. - for { - // Write to the ResponseWriter. - // Server Sent Events compatible. - ctx.Writef("data: %s\n\n", <-messageChan) - // or json: data:{obj}. - - // Flush the data immediately instead of buffering it for later. - flusher.Flush() - } -} - -type event struct { - Timestamp int64 `json:"timestamp"` - Message string `json:"message"` -} - -const script = `` - -func main() { - broker := NewBroker() - - go func() { - for { - time.Sleep(2 * time.Second) - - now := time.Now() - evt := event{ - Timestamp: now.Unix(), - Message: fmt.Sprintf("Hello at %s", now.Format(time.RFC1123)), - } - - evtBytes, err := json.Marshal(evt) - if err != nil { - golog.Error(err) - continue - } - - broker.Notifier <- evtBytes - } - }() - - app := iris.New() - app.Logger().SetLevel("debug") - - app.Get("/", func(ctx iris.Context) { - ctx.HTML( - `SSE` + script + ` - -

Waiting for messages...

- - - - - -
Timestamp (server)Message
- - `) - }) - - app.Get("/events", broker.ServeHTTP) - - // http://localhost:8080 - // http://localhost:8080/events - app.Listen(":8080") -} diff --git a/_examples/response-writer/sse/optional.sse.mini.js.html b/_examples/response-writer/sse/optional.sse.mini.js.html deleted file mode 100644 index 01e1304a63..0000000000 --- a/_examples/response-writer/sse/optional.sse.mini.js.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - SSE (javascript side) - - - - -

Open the browser's console(F12) and watch for incoming event messages

- - \ No newline at end of file diff --git a/_examples/response-writer/stream-writer/main.go b/_examples/response-writer/stream-writer/main.go deleted file mode 100644 index 58b2f11e42..0000000000 --- a/_examples/response-writer/stream-writer/main.go +++ /dev/null @@ -1,79 +0,0 @@ -package main - -import ( - "errors" - "io" - "time" // showcase the delay - - "github.com/kataras/iris/v12" -) - -var errDone = errors.New("done") - -func main() { - app := iris.New() - - app.Get("/", func(ctx iris.Context) { - ctx.ContentType("text/html") - ctx.Header("Transfer-Encoding", "chunked") - i := 0 - ints := []int{1, 2, 3, 5, 7, 9, 11, 13, 15, 17, 23, 29} - // Send the response in chunks and wait for half a second between each chunk, - // until connection close. - err := ctx.StreamWriter(func(w io.Writer) error { - ctx.Writef("Message number %d
", ints[i]) - time.Sleep(500 * time.Millisecond) // simulate delay. - if i == len(ints)-1 { - return errDone // ends the loop. - } - i++ - return nil // continue write - }) - - if err != errDone { - // Test it by canceling the request before the stream ends: - // [ERRO] $DATETIME stream: context canceled. - ctx.Application().Logger().Errorf("stream: %v", err) - } - }) - - type messageNumber struct { - Number int `json:"number"` - } - - app.Get("/json", func(ctx iris.Context) { - ctx.Header("Transfer-Encoding", "chunked") - i := 0 - ints := []int{1, 2, 3, 5, 7, 9, 11, 13, 15, 17, 23, 29} - // Send the response in chunks and wait for half a second between each chunk, - // until connection close. - notifyClose := ctx.Request().Context().Done() - for { - select { - case <-notifyClose: - // err := ctx.Request().Context().Err() - ctx.Application().Logger().Infof("Connection closed, loop end.") - return - default: - ctx.JSON(messageNumber{Number: ints[i]}) - ctx.WriteString("\n") - time.Sleep(500 * time.Millisecond) // simulate delay. - if i == len(ints)-1 { - ctx.Application().Logger().Infof("Loop end.") - return - } - i++ - ctx.ResponseWriter().Flush() - } - } - }) - - app.Listen(":8080") -} - -/* -Look the following methods too: -- Context.OnClose(callback) -- Context.OnConnectionClose(callback) and -- Context.Request().Context().Done()/.Err() too -*/ diff --git a/_examples/response-writer/transactions/main.go b/_examples/response-writer/transactions/main.go deleted file mode 100644 index 7b18b64d41..0000000000 --- a/_examples/response-writer/transactions/main.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" -) - -func main() { - app := iris.New() - - // subdomains works with all available routers, like other features too. - - app.Get("/", func(ctx iris.Context) { - ctx.BeginTransaction(func(t *context.Transaction) { - // OPTIONAl STEP: , if true then the next transictions will not be executed if this transiction fails - // t.SetScope(context.RequestTransactionScope) - - // OPTIONAL STEP: - // create a new custom type of error here to keep track of the status code and reason message - err := context.NewTransactionErrResult() - - // we should use t.Context if we want to rollback on any errors lives inside this function clojure. - t.Context().Text("Blablabla this should not be sent to the client because we will fill the err with a message and status") - - // virtualize a fake error here, for the sake of the example - fail := true - if fail { - err.StatusCode = iris.StatusInternalServerError - // NOTE: if empty reason then the default or the custom http error will be fired (like ctx.FireStatusCode) - err.Reason = "Error: Virtual failure!!" - } - - // OPTIONAl STEP: - // but useful if we want to post back an error message to the client if the transaction failed. - // if the reason is empty then the transaction completed successfully, - // otherwise we rollback the whole response writer's body, - // headers and cookies, status code and everything lives inside this transaction - t.Complete(err) - }) - - ctx.BeginTransaction(func(t *context.Transaction) { - t.Context().HTML("

This will sent at all cases because it lives on different transaction and it doesn't fails

") - // * if we don't have any 'throw error' logic then no need of scope.Complete() - }) - - // OPTIONALLY, depends on the usage: - // at any case, what ever happens inside the context's transactions send this to the client - ctx.HTML("

Let's add a second html message to the response, " + - "if the transaction was failed and it was request scoped then this message would " + - "not been shown. But it has a transient scope(default) so, it is visible as expected!

") - }) - - app.Listen(":8080") -} diff --git a/_examples/response-writer/write-rest/main.go b/_examples/response-writer/write-rest/main.go deleted file mode 100644 index 5e757827a8..0000000000 --- a/_examples/response-writer/write-rest/main.go +++ /dev/null @@ -1,125 +0,0 @@ -package main - -import ( - "encoding/xml" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" -) - -// User example struct for json and msgpack. -type User struct { - Firstname string `json:"firstname" msgpack:"firstname"` - Lastname string `json:"lastname" msgpack:"lastname"` - City string `json:"city" msgpack:"city"` - Age int `json:"age" msgpack:"age"` -} - -// ExampleXML just a test struct to view represents xml content-type -type ExampleXML struct { - XMLName xml.Name `xml:"example"` - One string `xml:"one,attr"` - Two string `xml:"two,attr"` -} - -// ExampleYAML just a test struct to write yaml to the client. -type ExampleYAML struct { - Name string `yaml:"name"` - ServerAddr string `yaml:"ServerAddr"` -} - -func main() { - app := iris.New() - - // Read - app.Post("/decode", func(ctx iris.Context) { - // Read https://github.com/kataras/iris/blob/master/_examples/request-body/read-json/main.go as well. - var user User - ctx.ReadJSON(&user) - - ctx.Writef("%s %s is %d years old and comes from %s!", user.Firstname, user.Lastname, user.Age, user.City) - }) - - // Write - app.Get("/encode", func(ctx iris.Context) { - u := User{ - Firstname: "John", - Lastname: "Doe", - City: "Neither FBI knows!!!", - Age: 25, - } - - // Manually setting a content type: ctx.ContentType("text/javascript") - ctx.JSON(u) - }) - - // Other content types, - - app.Get("/binary", func(ctx iris.Context) { - // useful when you want force-download of contents of raw bytes form. - ctx.Binary([]byte("Some binary data here.")) - }) - - app.Get("/text", func(ctx iris.Context) { - ctx.Text("Plain text here") - }) - - app.Get("/json", func(ctx iris.Context) { - ctx.JSON(map[string]string{"hello": "json"}) // or myjsonStruct{hello:"json} - }) - - app.Get("/jsonp", func(ctx iris.Context) { - ctx.JSONP(map[string]string{"hello": "jsonp"}, context.JSONP{Callback: "callbackName"}) - }) - - app.Get("/xml", func(ctx iris.Context) { - ctx.XML(ExampleXML{One: "hello", Two: "xml"}) - // OR: - // ctx.XML(iris.XMLMap("keys", iris.Map{"key": "value"})) - }) - - app.Get("/markdown", func(ctx iris.Context) { - ctx.Markdown([]byte("# Hello Dynamic Markdown -- iris")) - }) - - app.Get("/yaml", func(ctx iris.Context) { - ctx.YAML(ExampleYAML{Name: "Iris", ServerAddr: "localhost:8080"}) - // OR: - // ctx.YAML(iris.Map{"name": "Iris", "serverAddr": "localhost:8080"}) - }) - - // app.Get("/protobuf", func(ctx iris.Context) { - // ctx.Protobuf(proto.Message) - // }) - - app.Get("/msgpack", func(ctx iris.Context) { - u := User{ - Firstname: "John", - Lastname: "Doe", - City: "Neither FBI knows!!!", - Age: 25, - } - - ctx.MsgPack(u) - }) - - // http://localhost:8080/decode - // http://localhost:8080/encode - // - // http://localhost:8080/binary - // http://localhost:8080/text - // http://localhost:8080/json - // http://localhost:8080/jsonp - // http://localhost:8080/xml - // http://localhost:8080/markdown - // http://localhost:8080/msgpack - // - // `iris.WithOptimizations` is an optional configurator, - // if passed to the `Run` then it will ensure that the application - // response to the client as fast as possible. - // - // - // `iris.WithoutServerError` is an optional configurator, - // if passed to the `Run` then it will not print its passed error as an actual server error. - app.Listen(":8080", iris.WithOptimizations) -} diff --git a/_examples/routing/README.md b/_examples/routing/README.md deleted file mode 100644 index a5fb3d63b1..0000000000 --- a/_examples/routing/README.md +++ /dev/null @@ -1 +0,0 @@ -https://github.com/kataras/iris/wiki/Routing diff --git a/_examples/routing/basic/.dockerignore b/_examples/routing/basic/.dockerignore deleted file mode 100644 index ffdca42e81..0000000000 --- a/_examples/routing/basic/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -.git -node_modules -bin \ No newline at end of file diff --git a/_examples/routing/basic/Dockerfile b/_examples/routing/basic/Dockerfile deleted file mode 100644 index 09daa324c3..0000000000 --- a/_examples/routing/basic/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# docker build -t myapp . -# docker run --rm -it -p 8080:8080 myapp:latest -FROM golang:latest AS builder -RUN apt-get update -ENV GO111MODULE=on \ - CGO_ENABLED=0 \ - GOOS=linux \ - GOARCH=amd64 -WORKDIR /go/src/app -COPY go.mod . -RUN go mod download -COPY . . -RUN go install - -FROM scratch -COPY --from=builder /go/bin/app . -ENTRYPOINT ["./app"] \ No newline at end of file diff --git a/_examples/routing/basic/README.md b/_examples/routing/basic/README.md deleted file mode 100644 index 350562a921..0000000000 --- a/_examples/routing/basic/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Basic Example - -The only requirement for this example is [Docker](https://docs.docker.com/install/). - -## Docker Compose - -The Docker Compose is pre-installed with Docker for Windows. For linux please follow the steps described at: https://docs.docker.com/compose/install/. - -Build and run the application for linux arch and expose it on http://localhost:8080. - -```sh -$ docker-compose up -``` - -See [docker-compose file](docker-compose.yml). - -## Without Docker Compose - -1. Build the image as "myapp" (docker build) -2. Run the image and map exposed ports (-p 8080:8080) -3. Attach the interactive mode so CTRL/CMD+C signals are respected to shutdown the Iris Server (-it) -4. Cleanup the image on finish (--rm) - -```sh -$ docker build -t myapp . -$ docker run --rm -it -p 8080:8080 myapp:latest -``` diff --git a/_examples/routing/basic/docker-compose.yml b/_examples/routing/basic/docker-compose.yml deleted file mode 100644 index 54c86b33cb..0000000000 --- a/_examples/routing/basic/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -# docker-compose up [--build] -version: '3' - -services: - app: - build: . - ports: - - 8080:8080 \ No newline at end of file diff --git a/_examples/routing/basic/main.go b/_examples/routing/basic/main.go deleted file mode 100644 index 1ba67a82f8..0000000000 --- a/_examples/routing/basic/main.go +++ /dev/null @@ -1,206 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func newApp() *iris.Application { - app := iris.New() - app.Logger().SetLevel("debug") - - // registers a custom handler for 404 not found http (error) status code, - // fires when route not found or manually by ctx.StatusCode(iris.StatusNotFound). - app.OnErrorCode(iris.StatusNotFound, notFoundHandler) - - // GET -> HTTP Method - // / -> Path - // func(ctx iris.Context) -> The route's handler. - // - // Third receiver should contains the route's handler(s), they are executed by order. - app.Handle("GET", "/", func(ctx iris.Context) { - // navigate to the https://github.com/kataras/iris/wiki/Routing-context-methods - // to overview all context's method. - ctx.HTML("Hello from " + ctx.Path()) // Hello from / - }) - - app.Get("/home", func(ctx iris.Context) { - ctx.Writef(`Same as app.Handle("GET", "/", [...])`) - }) - - // Different path parameters types in the same path. - // Note that: fallback should registered first e.g. {path} {string}, - // because the handler on this case is executing from last to top. - app.Get("/u/{p:path}", func(ctx iris.Context) { - ctx.Writef(":string, :int, :uint, :alphabetical and :path in the same path pattern.") - }) - - app.Get("/u/{username:string}", func(ctx iris.Context) { - ctx.Writef("before username (string), current route name: %s\n", ctx.RouteName()) - ctx.Next() - }, func(ctx iris.Context) { - ctx.Writef("username (string): %s", ctx.Params().Get("username")) - }) - - app.Get("/u/{firstname:alphabetical}", func(ctx iris.Context) { - ctx.Writef("before firstname (alphabetical), current route name: %s\n", ctx.RouteName()) - ctx.Next() - }, func(ctx iris.Context) { - ctx.Writef("firstname (alphabetical): %s", ctx.Params().Get("firstname")) - }) - - app.Get("/u/{id:int}", func(ctx iris.Context) { - ctx.Writef("before id (int), current route name: %s\n", ctx.RouteName()) - ctx.Next() - }, func(ctx iris.Context) { - ctx.Writef("id (int): %d", ctx.Params().GetIntDefault("id", 0)) - }) - - app.Get("/u/{uid:uint}", func(ctx iris.Context) { - ctx.Writef("before uid (uint), current route name: %s\n", ctx.RouteName()) - ctx.Next() - }, func(ctx iris.Context) { - ctx.Writef("uid (uint): %d", ctx.Params().GetUintDefault("uid", 0)) - }) - - /* - /u/some/path/here maps to :path - /u/abcd maps to :alphabetical (if :alphabetical registered otherwise :string) - /u/42 maps to :uint (if :uint registered otherwise :int) - /u/-1 maps to :int (if :int registered otherwise :string) - /u/abcd123 maps to :string - */ - - // Pssst, don't forget dynamic-path example for more "magic"! - app.Get("/api/users/{userid:uint64 min(1)}", func(ctx iris.Context) { - userID, err := ctx.Params().GetUint64("userid") - if err != nil { - ctx.Writef("error while trying to parse userid parameter," + - "this will never happen if :uint64 is being used because if it's not a valid uint64 it will fire Not Found automatically.") - ctx.StatusCode(iris.StatusBadRequest) - return - } - - ctx.JSON(map[string]interface{}{ - // you can pass any custom structured go value of course. - "user_id": userID, - }) - }) - // app.Post("/", func(ctx iris.Context){}) -> for POST http method. - // app.Put("/", func(ctx iris.Context){})-> for "PUT" http method. - // app.Delete("/", func(ctx iris.Context){})-> for "DELETE" http method. - // app.Options("/", func(ctx iris.Context){})-> for "OPTIONS" http method. - // app.Trace("/", func(ctx iris.Context){})-> for "TRACE" http method. - // app.Head("/", func(ctx iris.Context){})-> for "HEAD" http method. - // app.Connect("/", func(ctx iris.Context){})-> for "CONNECT" http method. - // app.Patch("/", func(ctx iris.Context){})-> for "PATCH" http method. - // app.Any("/", func(ctx iris.Context){}) for all http methods. - - // More than one route can contain the same path with a different http mapped method. - // You can catch any route creation errors with: - // route, err := app.Get(...) - // set a name to a route: route.Name = "myroute" - - // You can also group routes by path prefix, sharing middleware(s) and done handlers. - - adminRoutes := app.Party("/admin", adminMiddleware) - - adminRoutes.Done(func(ctx iris.Context) { // executes always last if ctx.Next() - ctx.Application().Logger().Infof("response sent to " + ctx.Path()) - }) - // adminRoutes.Layout("/views/layouts/admin.html") // set a view layout for these routes, see more at view examples. - - // GET: http://localhost:8080/admin - adminRoutes.Get("/", func(ctx iris.Context) { - // [...] - ctx.StatusCode(iris.StatusOK) // default is 200 == iris.StatusOK - ctx.HTML("

Hello from admin/

") - - ctx.Next() // in order to execute the party's "Done" Handler(s) - }) - - // GET: http://localhost:8080/admin/login - adminRoutes.Get("/login", func(ctx iris.Context) { - // [...] - }) - // POST: http://localhost:8080/admin/login - adminRoutes.Post("/login", func(ctx iris.Context) { - // [...] - }) - - // subdomains, easier than ever, should add localhost or 127.0.0.1 into your hosts file, - // etc/hosts on unix or C:/windows/system32/drivers/etc/hosts on windows. - v1 := app.Party("v1.") - { // braces are optional, it's just type of style, to group the routes visually. - - // http://v1.localhost:8080 - // Note: for versioning-specific features checkout the _examples/routing/versioning instead. - v1.Get("/", func(ctx iris.Context) { - ctx.HTML(`Version 1 API. go to /api/users`) - }) - - usersAPI := v1.Party("/api/users") - { - // http://v1.localhost:8080/api/users - usersAPI.Get("/", func(ctx iris.Context) { - ctx.Writef("All users") - }) - // http://v1.localhost:8080/api/users/42 - usersAPI.Get("/{userid:int}", func(ctx iris.Context) { - ctx.Writef("user with id: %d", ctx.Params().GetIntDefault("userid", 0)) - }) - } - } - - // wildcard subdomains. - wildcardSubdomain := app.Party("*.") - { - wildcardSubdomain.Get("/", func(ctx iris.Context) { - ctx.Writef("Subdomain can be anything, now you're here from: %s", ctx.Subdomain()) - }) - } - - return app -} - -func main() { - app := newApp() - - // http://localhost:8080 - // http://localhost:8080/home - // http://localhost:8080/api/users/42 - // http://localhost:8080/admin - // http://localhost:8080/admin/login - // - // http://localhost:8080/api/users/0 - // http://localhost:8080/api/users/blabla - // http://localhost:8080/wontfound - // - // http://localhost:8080/u/abcd - // http://localhost:8080/u/42 - // http://localhost:8080/u/-1 - // http://localhost:8080/u/abcd123 - // http://localhost:8080/u/some/path/here - // - // if hosts edited: - // http://v1.localhost:8080 - // http://v1.localhost:8080/api/users - // http://v1.localhost:8080/api/users/42 - // http://anything.localhost:8080 - app.Listen(":8080") -} - -func adminMiddleware(ctx iris.Context) { - // [...] - ctx.Next() // to move to the next handler, or don't that if you have any auth logic. -} - -func notFoundHandler(ctx iris.Context) { - ctx.HTML("Custom route for 404 not found http code, here you can render a view, html, json any valid response.") -} - -// Notes: -// A path parameter name should contain only alphabetical letters, symbols, containing '_' and numbers are NOT allowed. -// If route failed to be registered, the app will panic without any warnings -// if you didn't catch the second return value(error) on .Handle/.Get.... - -// See "file-server/single-page-application" to see how another feature, "WrapRouter", works. diff --git a/_examples/routing/basic/main_test.go b/_examples/routing/basic/main_test.go deleted file mode 100644 index 5756fb3d64..0000000000 --- a/_examples/routing/basic/main_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package main - -import ( - "fmt" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -// Shows a very basic usage of the httptest. -// The tests are written in a way to be easy to understand, -// for a more comprehensive testing examples check out the: -// _examples/routing/main_test.go, -// _examples/routing/subdomains/www/main_test.go -// _examples/file-server and e.t.c. -// Almost every example which covers -// a new feature from you to learn -// contains a test file as well. -func TestRoutingBasic(t *testing.T) { - expectedUResponse := func(paramName, paramType, paramValue string) string { - s := fmt.Sprintf("before %s (%s), current route name: GET/u/{%s:%s}\n", paramName, paramType, paramName, paramType) - s += fmt.Sprintf("%s (%s): %s", paramName, paramType, paramValue) - return s - } - - var ( - expectedNotFoundResponse = "Custom route for 404 not found http code, here you can render a view, html, json any valid response." - - expectedIndexResponse = "Hello from /" - expectedHomeResponse = `Same as app.Handle("GET", "/", [...])` - - expectedUpathResponse = ":string, :int, :uint, :alphabetical and :path in the same path pattern." - expectedUStringResponse = expectedUResponse("username", "string", "abcd123") - expectedUIntResponse = expectedUResponse("id", "int", "-1") - expectedUUintResponse = expectedUResponse("uid", "uint", "42") - expectedUAlphabeticalResponse = expectedUResponse("firstname", "alphabetical", "abcd") - - expectedAPIUsersIndexResponse = map[string]interface{}{"user_id": 42} - - expectedAdminIndexResponse = "

Hello from admin/

" - - expectedSubdomainV1IndexResponse = `Version 1 API. go to /api/users` - expectedSubdomainV1APIUsersIndexResponse = "All users" - expectedSubdomainV1APIUsersIndexWithParamResponse = "user with id: 42" - - expectedSubdomainWildcardIndexResponse = "Subdomain can be anything, now you're here from: any-subdomain-here" - ) - - app := newApp() - e := httptest.New(t, app) - - e.GET("/anotfound").Expect().Status(httptest.StatusNotFound). - Body().Equal(expectedNotFoundResponse) - - e.GET("/").Expect().Status(httptest.StatusOK). - Body().Equal(expectedIndexResponse) - e.GET("/home").Expect().Status(httptest.StatusOK). - Body().Equal(expectedHomeResponse) - - e.GET("/u/some/path/here").Expect().Status(httptest.StatusOK). - Body().Equal(expectedUpathResponse) - e.GET("/u/abcd123").Expect().Status(httptest.StatusOK). - Body().Equal(expectedUStringResponse) - e.GET("/u/-1").Expect().Status(httptest.StatusOK). - Body().Equal(expectedUIntResponse) - e.GET("/u/42").Expect().Status(httptest.StatusOK). - Body().Equal(expectedUUintResponse) - e.GET("/u/abcd").Expect().Status(httptest.StatusOK). - Body().Equal(expectedUAlphabeticalResponse) - - e.GET("/api/users/42").Expect().Status(httptest.StatusOK). - JSON().Equal(expectedAPIUsersIndexResponse) - - e.GET("/admin").Expect().Status(httptest.StatusOK). - Body().Equal(expectedAdminIndexResponse) - - e.Request("GET", "/").WithURL("http://v1.example.com").Expect().Status(httptest.StatusOK). - Body().Equal(expectedSubdomainV1IndexResponse) - - e.Request("GET", "/api/users").WithURL("http://v1.example.com").Expect().Status(httptest.StatusOK). - Body().Equal(expectedSubdomainV1APIUsersIndexResponse) - - e.Request("GET", "/api/users/42").WithURL("http://v1.example.com").Expect().Status(httptest.StatusOK). - Body().Equal(expectedSubdomainV1APIUsersIndexWithParamResponse) - - e.Request("GET", "/").WithURL("http://any-subdomain-here.example.com").Expect().Status(httptest.StatusOK). - Body().Equal(expectedSubdomainWildcardIndexResponse) -} diff --git a/_examples/routing/conditional-chain/main.go b/_examples/routing/conditional-chain/main.go deleted file mode 100644 index 4d0747f6ef..0000000000 --- a/_examples/routing/conditional-chain/main.go +++ /dev/null @@ -1,56 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func newApp() *iris.Application { - app := iris.New() - v1 := app.Party("/api/v1") - - myFilter := func(ctx iris.Context) bool { - // don't do that on production, use session or/and database calls and etc. - ok, _ := ctx.URLParamBool("admin") - return ok - } - - onlyWhenFilter1 := func(ctx iris.Context) { - ctx.Application().Logger().Infof("admin: %#+v", ctx.URLParams()) - ctx.Writef("Admin\n") - ctx.Next() - } - - onlyWhenFilter2 := func(ctx iris.Context) { - // You can always use the per-request storage - // to perform actions like this ofc. - // - // this handler: ctx.Values().Set("is_admin", true) - // next handler: isAdmin := ctx.Values().GetBoolDefault("is_admin", false) - // - // but, let's simplify it: - ctx.HTML("

Hello Admin


") - ctx.Next() - } - - // HERE: - // It can be registered anywhere, as a middleware. - // It will fire the `onlyWhenFilter1` and `onlyWhenFilter2` as middlewares (with ctx.Next()) - // if myFilter pass otherwise it will just continue the handler chain with ctx.Next() by ignoring - // the `onlyWhenFilter1` and `onlyWhenFilter2`. - myMiddleware := iris.NewConditionalHandler(myFilter, onlyWhenFilter1, onlyWhenFilter2) - - v1UsersRouter := v1.Party("/users", myMiddleware) - v1UsersRouter.Get("/", func(ctx iris.Context) { - ctx.HTML("requested: /api/v1/users") - }) - - return app -} - -func main() { - app := newApp() - - // http://localhost:8080/api/v1/users - // http://localhost:8080/api/v1/users?admin=true - app.Listen(":8080") -} diff --git a/_examples/routing/conditional-chain/main_test.go b/_examples/routing/conditional-chain/main_test.go deleted file mode 100644 index ca0bb139f0..0000000000 --- a/_examples/routing/conditional-chain/main_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestNewConditionalHandler(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - e.GET("/api/v1/users").Expect().Status(httptest.StatusOK). - Body().Equal("requested: /api/v1/users") - e.GET("/api/v1/users").WithQuery("admin", "true").Expect().Status(httptest.StatusOK). - Body().Equal("Admin\n

Hello Admin


requested: /api/v1/users") -} diff --git a/_examples/routing/custom-router/main.go b/_examples/routing/custom-router/main.go deleted file mode 100644 index 3ec24fe258..0000000000 --- a/_examples/routing/custom-router/main.go +++ /dev/null @@ -1,108 +0,0 @@ -package main - -import ( - "strings" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/core/router" -) - -/* A Router should contain all three of the following methods: - - HandleRequest should handle the request based on the Context. - HandleRequest(ctx iris.Context) - - Build should builds the handler, it's being called on router's BuildRouter. - Build(provider router.RoutesProvider) error - - RouteExists reports whether a particular route exists. - RouteExists(ctx iris.Context, method, path string) bool - - FireErrorCode(ctx iris.Context) should handle the given ctx.GetStatusCode(). - -For a more detailed, complete and useful example -you can take a look at the iris' router itself which is located at: -https://github.com/kataras/iris/tree/master/core/router/handler.go -which completes this exact interface, the `router#RequestHandler`. -*/ -type customRouter struct { - // a copy of routes (safer because you will not be able to alter a route on serve-time without a `app.RefreshRouter` call): - // []router.Route - // or just expect the whole routes provider: - provider router.RoutesProvider -} - -// HandleRequest a silly example which finds routes based only on the first part of the requested path -// which must be a static one as well, the rest goes to fill the parameters. -func (r *customRouter) HandleRequest(ctx iris.Context) { - path := ctx.Path() - ctx.Application().Logger().Infof("Requested resource path: %s", path) - - parts := strings.Split(path, "/")[1:] - staticPath := "/" + parts[0] - for _, route := range r.provider.GetRoutes() { - if strings.HasPrefix(route.Path, staticPath) && route.Method == ctx.Method() { - paramParts := parts[1:] - for _, paramValue := range paramParts { - for _, p := range route.Tmpl().Params { - ctx.Params().Set(p.Name, paramValue) - } - } - - ctx.SetCurrentRoute(route.ReadOnly) - ctx.Do(route.Handlers) - return - } - } - - // if nothing found... - ctx.StatusCode(iris.StatusNotFound) -} - -func (r *customRouter) Build(provider router.RoutesProvider) error { - for _, route := range provider.GetRoutes() { - // do any necessary validation or conversations based on your custom logic here - // but always run the "BuildHandlers" for each registered route. - route.BuildHandlers() - // [...] r.routes = append(r.routes, *route) - } - - r.provider = provider - return nil -} - -func (r *customRouter) RouteExists(ctx iris.Context, method, path string) bool { - // [...] - return false -} - -func (r *customRouter) FireErrorCode(ctx iris.Context) { - // responseStatusCode := ctx.GetStatusCode() // set by prior ctx.StatusCode calls - // [...] -} - -func main() { - app := iris.New() - - // In case you are wondering, the parameter types and macros like "{param:string $func()}" still work inside - // your custom router if you fetch by the Route's Handler - // because they are middlewares under the hood, so you don't have to implement the logic of handling them manually, - // though you have to match what requested path is what route and fill the ctx.Params(), this is the work of your custom router. - app.Get("/hello/{name}", func(ctx iris.Context) { - name := ctx.Params().Get("name") - ctx.Writef("Hello %s\n", name) - }) - - app.Get("/cs/{num:uint64 min(10) else 400}", func(ctx iris.Context) { - num := ctx.Params().GetUint64Default("num", 0) - ctx.Writef("num is: %d\n", num) - }) - - // To replace the existing router with a customized one by using the iris/context.Context - // you have to use the `app.BuildRouter` method before `app.Run` and after the routes registered. - // You should pass your custom router's instance as the second input arg, which must completes the `router#RequestHandler` - // interface as shown above. - // - // To see how you can build something even more low-level without direct iris' context support (you can do that manually as well) - // navigate to the "custom-wrapper" example instead. - myCustomRouter := new(customRouter) - app.BuildRouter(app.ContextPool, myCustomRouter, app.APIBuilder, true) - - app.Listen(":8080") -} diff --git a/_examples/routing/custom-wrapper/main.go b/_examples/routing/custom-wrapper/main.go deleted file mode 100644 index 9f18cdf6ed..0000000000 --- a/_examples/routing/custom-wrapper/main.go +++ /dev/null @@ -1,69 +0,0 @@ -package main - -import ( - "net/http" - "strings" - - "github.com/kataras/iris/v12" -) - -// In this example you'll just see one use case of .WrapRouter. -// You can use the .WrapRouter to add custom logic when or when not the router should -// be executed in order to execute the registered routes' handlers. -func newApp() *iris.Application { - app := iris.New() - - app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) { - ctx.HTML("Resource Not found") - }) - - app.Get("/profile/{username}", func(ctx iris.Context) { - ctx.Writef("Hello %s", ctx.Params().Get("username")) - }) - - app.HandleDir("/", iris.Dir("./public")) - - myOtherHandler := func(ctx iris.Context) { - ctx.Writef("inside a handler which is fired manually by our custom router wrapper") - } - - // wrap the router with a native net/http handler. - // if url does not contain any "." (i.e: .css, .js...) - // (depends on the app , you may need to add more file-server exceptions), - // then the handler will execute the router that is responsible for the - // registered routes (look "/" and "/profile/{username}") - // if not then it will serve the files based on the root "/" path. - app.WrapRouter(func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) { - path := r.URL.Path - - if strings.HasPrefix(path, "/other") { - // acquire and release a context in order to use it to execute - // our custom handler - // remember: we use net/http.Handler because here we are in the "low-level", before the router itself. - ctx := app.ContextPool.Acquire(w, r) - myOtherHandler(ctx) - app.ContextPool.Release(ctx) - return - } - - router.ServeHTTP(w, r) // else continue serving routes as usual. - }) - - return app -} - -func main() { - app := newApp() - - // http://localhost:8080 - // http://localhost:8080/index.html - // http://localhost:8080/app.js - // http://localhost:8080/css/main.css - // http://localhost:8080/profile/anyusername - // http://localhost:8080/other/random - app.Listen(":8080") - - // Note: In this example we just saw one use case, - // you may want to .WrapRouter or .Downgrade in order to bypass the iris' default router, i.e: - // you can use that method to setup custom proxies too. -} diff --git a/_examples/routing/custom-wrapper/main_test.go b/_examples/routing/custom-wrapper/main_test.go deleted file mode 100644 index 540f0435b3..0000000000 --- a/_examples/routing/custom-wrapper/main_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package main - -import ( - "io/ioutil" - "path/filepath" - "strings" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -type resource string - -func (r resource) String() string { - return string(r) -} - -func (r resource) strip(strip string) string { - s := r.String() - return strings.TrimPrefix(s, strip) -} - -func (r resource) loadFromBase(dir string) string { - filename := r.String() - - if filename == "/" { - filename = "/index.html" - } - - fullpath := filepath.Join(dir, filename) - - b, err := ioutil.ReadFile(fullpath) - if err != nil { - panic(fullpath + " failed with error: " + err.Error()) - } - - return string(b) -} - -var urls = []resource{ - "/", - "/index.html", - "/app.js", - "/css/main.css", -} - -func TestCustomWrapper(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - for _, u := range urls { - url := u.String() - contents := u.loadFromBase("./public") - - e.GET(url).Expect(). - Status(httptest.StatusOK). - Body().Equal(contents) - } - - e.GET("/other/something").Expect().Status(httptest.StatusOK) -} diff --git a/_examples/routing/custom-wrapper/public/app.js b/_examples/routing/custom-wrapper/public/app.js deleted file mode 100644 index c47a1fd53e..0000000000 --- a/_examples/routing/custom-wrapper/public/app.js +++ /dev/null @@ -1 +0,0 @@ -window.alert("app.js loaded from \"/"); \ No newline at end of file diff --git a/_examples/routing/custom-wrapper/public/css/main.css b/_examples/routing/custom-wrapper/public/css/main.css deleted file mode 100644 index fb72e54a97..0000000000 --- a/_examples/routing/custom-wrapper/public/css/main.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: black; -} diff --git a/_examples/routing/custom-wrapper/public/index.html b/_examples/routing/custom-wrapper/public/index.html deleted file mode 100644 index 960869d7da..0000000000 --- a/_examples/routing/custom-wrapper/public/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - Index Page - - - -

Hello from index.html

- - - - - - \ No newline at end of file diff --git a/_examples/routing/dynamic-path/main.go b/_examples/routing/dynamic-path/main.go deleted file mode 100644 index 8cc0ce733d..0000000000 --- a/_examples/routing/dynamic-path/main.go +++ /dev/null @@ -1,293 +0,0 @@ -package main - -import ( - "regexp" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - // At the previous example "routing/basic", - // we've seen static routes, group of routes, subdomains, wildcard subdomains, a small example of parameterized path - // with a single known paramete and custom http errors, now it's time to see wildcard parameters and macros. - - // Iris, like net/http std package registers route's handlers - // by a Handler, the iris' type of handler is just a func(ctx iris.Context) - // where context comes from github.com/kataras/iris/context. - // - // Iris has the easiest and the most powerful routing process you have ever meet. - // - // At the same time, - // Iris has its own interpeter(yes like a programming language) - // for route's path syntax and their dynamic path parameters parsing and evaluation, - // We call them "macros" for shortcut. - // How? It calculates its needs and if not any special regexp needed then it just - // registers the route with the low-level underline path syntax, - // otherwise it pre-compiles the regexp and adds the necessary middleware(s). - // - // Standard macro types for parameters: - // +------------------------+ - // | {param:string} | - // +------------------------+ - // string type - // anything (single path segmnent) - // - // +-------------------------------+ - // | {param:int} | - // +-------------------------------+ - // int type - // -9223372036854775808 to 9223372036854775807 (x64) or -2147483648 to 2147483647 (x32), depends on the host arch - // - // +------------------------+ - // | {param:int8} | - // +------------------------+ - // int8 type - // -128 to 127 - // - // +------------------------+ - // | {param:int16} | - // +------------------------+ - // int16 type - // -32768 to 32767 - // - // +------------------------+ - // | {param:int32} | - // +------------------------+ - // int32 type - // -2147483648 to 2147483647 - // - // +------------------------+ - // | {param:int64} | - // +------------------------+ - // int64 type - // -9223372036854775808 to 9223372036854775807 - // - // +------------------------+ - // | {param:uint} | - // +------------------------+ - // uint type - // 0 to 18446744073709551615 (x64) or 0 to 4294967295 (x32) - // - // +------------------------+ - // | {param:uint8} | - // +------------------------+ - // uint8 type - // 0 to 255 - // - // +------------------------+ - // | {param:uint16} | - // +------------------------+ - // uint16 type - // 0 to 65535 - // - // +------------------------+ - // | {param:uint32} | - // +------------------------+ - // uint32 type - // 0 to 4294967295 - // - // +------------------------+ - // | {param:uint64} | - // +------------------------+ - // uint64 type - // 0 to 18446744073709551615 - // - // +---------------------------------+ - // | {param:bool} or {param:boolean} | - // +---------------------------------+ - // bool type - // only "1" or "t" or "T" or "TRUE" or "true" or "True" - // or "0" or "f" or "F" or "FALSE" or "false" or "False" - // - // +------------------------+ - // | {param:alphabetical} | - // +------------------------+ - // alphabetical/letter type - // letters only (upper or lowercase) - // - // +------------------------+ - // | {param:file} | - // +------------------------+ - // file type - // letters (upper or lowercase) - // numbers (0-9) - // underscore (_) - // dash (-) - // point (.) - // no spaces ! or other character - // - // +------------------------+ - // | {param:path} | - // +------------------------+ - // path type - // anything, should be the last part, can be more than one path segment, - // i.e: "/test/{param:path}" and request: "/test/path1/path2/path3" , ctx.Params().Get("param") == "path1/path2/path3" - // - // if type is missing then parameter's type is defaulted to string, so - // {param} == {param:string}. - // - // If a function not found on that type then the `string` macro type's functions are being used. - // - // - // Besides the fact that iris provides the basic types and some default "macro funcs" - // you are able to register your own too!. - // - // Register a named path parameter function: - // app.Macros().Number.RegisterFunc("min", func(argument int) func(paramValue string) bool { - // [...] - // return true/false -> true means valid. - // }) - // - // at the func(argument ...) you can have any standard type, it will be validated before the server starts - // so don't care about performance here, the only thing it runs at serve time is the returning func(paramValue string) bool. - // - // {param:string equal(iris)} , "iris" will be the argument here: - // app.Macros().String.RegisterFunc("equal", func(argument string) func(paramValue string) bool { - // return func(paramValue string) bool { return argument == paramValue } - // }) - - // you can use the "string" type which is valid for a single path parameter that can be anything. - app.Get("/username/{name}", func(ctx iris.Context) { - ctx.Writef("Hello %s", ctx.Params().Get("name")) - }) // type is missing = {name:string} - - // Let's register our first macro attached to uint64 macro type. - // "min" = the function - // "minValue" = the argument of the function - // func(uint64) bool = our func's evaluator, this executes in serve time when - // a user requests a path which contains the :uint64 macro parameter type with the min(...) macro parameter function. - app.Macros().Get("uint64").RegisterFunc("min", func(minValue uint64) func(uint64) bool { - // type of "paramValue" should match the type of the internal macro's evaluator function, which in this case is "uint64". - return func(paramValue uint64) bool { - return paramValue >= minValue - } - }) - - // http://localhost:8080/profile/id>=20 - // this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1 - // macro parameter functions are optional of course. - app.Get("/profile/{id:uint64 min(20)}", func(ctx iris.Context) { - // second parameter is the error but it will always nil because we use macros, - // the validaton already happened. - id := ctx.Params().GetUint64Default("id", 0) - ctx.Writef("Hello id: %d", id) - }) - - // to change the error code per route's macro evaluator: - app.Get("/profile/{id:uint64 min(1)}/friends/{friendid:uint64 min(1) else 504}", func(ctx iris.Context) { - id := ctx.Params().GetUint64Default("id", 0) - friendid := ctx.Params().GetUint64Default("friendid", 0) - ctx.Writef("Hello id: %d looking for friend id: %d", id, friendid) - }) // this will throw e 504 error code instead of 404 if all route's macros not passed. - - // :uint8 0 to 255. - app.Get("/ages/{age:uint8 else 400}", func(ctx iris.Context) { - age, _ := ctx.Params().GetUint8("age") - ctx.Writef("age selected: %d", age) - }) - - // Another example using a custom regexp or any custom logic. - - // Register your custom argument-less macro function to the :string param type. - latLonExpr := "^-?[0-9]{1,3}(?:\\.[0-9]{1,10})?$" - latLonRegex, err := regexp.Compile(latLonExpr) - if err != nil { - panic(err) - } - - // MatchString is a type of func(string) bool, so we use it as it is. - app.Macros().Get("string").RegisterFunc("coordinate", latLonRegex.MatchString) - - app.Get("/coordinates/{lat:string coordinate() else 502}/{lon:string coordinate() else 502}", func(ctx iris.Context) { - ctx.Writef("Lat: %s | Lon: %s", ctx.Params().Get("lat"), ctx.Params().Get("lon")) - }) - - // - - // Another one is by using a custom body. - app.Macros().Get("string").RegisterFunc("range", func(minLength, maxLength int) func(string) bool { - return func(paramValue string) bool { - return len(paramValue) >= minLength && len(paramValue) <= maxLength - } - }) - - app.Get("/limitchar/{name:string range(1,200)}", func(ctx iris.Context) { - name := ctx.Params().Get("name") - ctx.Writef(`Hello %s | the name should be between 1 and 200 characters length - otherwise this handler will not be executed`, name) - }) - - // - - // Register your custom macro function which accepts a slice of strings `[...,...]`. - app.Macros().Get("string").RegisterFunc("has", func(validNames []string) func(string) bool { - return func(paramValue string) bool { - for _, validName := range validNames { - if validName == paramValue { - return true - } - } - - return false - } - }) - - app.Get("/static_validation/{name:string has([kataras,gerasimos,maropoulos])}", func(ctx iris.Context) { - name := ctx.Params().Get("name") - ctx.Writef(`Hello %s | the name should be "kataras" or "gerasimos" or "maropoulos" - otherwise this handler will not be executed`, name) - }) - - // - - // http://localhost:8080/game/a-zA-Z/level/42 - // remember, alphabetical is lowercase or uppercase letters only. - app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx iris.Context) { - ctx.Writef("name: %s | level: %s", ctx.Params().Get("name"), ctx.Params().Get("level")) - }) - - app.Get("/lowercase/static", func(ctx iris.Context) { - ctx.Writef("static and dynamic paths are not conflicted anymore!") - }) - - // let's use a trivial custom regexp that validates a single path parameter - // which its value is only lowercase letters. - - // http://localhost:8080/lowercase/anylowercase - app.Get("/lowercase/{name:string regexp(^[a-z]+)}", func(ctx iris.Context) { - ctx.Writef("name should be only lowercase, otherwise this handler will never executed: %s", ctx.Params().Get("name")) - }) - - // http://localhost:8080/single_file/app.js - app.Get("/single_file/{myfile:file}", func(ctx iris.Context) { - ctx.Writef("file type validates if the parameter value has a form of a file name, got: %s", ctx.Params().Get("myfile")) - }) - - // http://localhost:8080/myfiles/any/directory/here/ - // this is the only macro type that accepts any number of path segments. - app.Get("/myfiles/{directory:path}", func(ctx iris.Context) { - ctx.Writef("path type accepts any number of path segments, path after /myfiles/ is: %s", ctx.Params().Get("directory")) - }) // for wildcard path (any number of path segments) without validation you can use: - // /myfiles/* - - // "{param}"'s performance is exactly the same of ":param"'s. - - // alternatives -> ":param" for single path parameter and "*" for wildcard path parameter. - // Note these: - // if "/mypath/*" then the parameter name is "*". - // if "/mypath/{myparam:path}" then the parameter has two names, one is the "*" and the other is the user-defined "myparam". - - // WARNING: - // A path parameter name should contain only alphabetical letters or digits. Symbols like '_' are NOT allowed. - // Last, do not confuse `ctx.Params()` with `ctx.Values()`. - // Path parameter's values can be retrieved from `ctx.Params()`, - // context's local storage that can be used to communicate between handlers and middleware(s) can be stored to `ctx.Values()`. - // - // When registering different parameter types in the same exact path pattern, the path parameter's name - // should differ e.g. - // /path/{name:string} - // /path/{id:uint} - app.Listen(":8080") -} diff --git a/_examples/routing/dynamic-path/root-wildcard/main.go b/_examples/routing/dynamic-path/root-wildcard/main.go deleted file mode 100644 index 2ccb848906..0000000000 --- a/_examples/routing/dynamic-path/root-wildcard/main.go +++ /dev/null @@ -1,70 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - // this works as expected now, - // will handle all GET requests - // except: - // / -> because of app.Get("/", ...) - // /other/anything/here -> because of app.Get("/other/{paramother:path}", ...) - // /other2/anything/here -> because of app.Get("/other2/{paramothersecond:path}", ...) - // /other2/static2 -> because of app.Get("/other2/static", ...) - // - // It isn't conflicts with the rest of the routes, without routing performance cost! - // - // i.e /something/here/that/cannot/be/found/by/other/registered/routes/order/not/matters - app.Get("/{p:path}", h) - // app.Get("/static/{p:path}", staticWildcardH) - - // this will handle only GET / - app.Get("/", staticPath) - - // this will handle all GET requests starting with "/other/" - // - // i.e /other/more/than/one/path/parts - app.Get("/other/{paramother:path}", other) - - // this will handle all GET requests starting with "/other2/" - // except /other2/static (because of the next static route) - // - // i.e /other2/more/than/one/path/parts - app.Get("/other2/{paramothersecond:path}", other2) - - // this will handle only GET "/other2/static" - app.Get("/other2/static2", staticPathOther2) - - app.Listen(":8080") -} - -func h(ctx iris.Context) { - param := ctx.Params().Get("p") - ctx.WriteString(param) -} - -func staticWildcardH(ctx iris.Context) { - param := ctx.Params().Get("p") - ctx.WriteString("from staticWildcardH: param=" + param) -} - -func other(ctx iris.Context) { - param := ctx.Params().Get("paramother") - ctx.Writef("from other: %s", param) -} - -func other2(ctx iris.Context) { - param := ctx.Params().Get("paramothersecond") - ctx.Writef("from other2: %s", param) -} - -func staticPath(ctx iris.Context) { - ctx.Writef("from the static path(/): %s", ctx.Path()) -} - -func staticPathOther2(ctx iris.Context) { - ctx.Writef("from the static path(/other2/static2): %s", ctx.Path()) -} diff --git a/_examples/routing/dynamic-path/same-pattern-different-func/main.go b/_examples/routing/dynamic-path/same-pattern-different-func/main.go deleted file mode 100644 index e241a51bfc..0000000000 --- a/_examples/routing/dynamic-path/same-pattern-different-func/main.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := newApp() - app.Logger().SetLevel("debug") - app.Listen(":8080") -} - -func newApp() *iris.Application { - app := iris.New() - - app.HandleMany(iris.MethodGet, "/ /api/{page:string suffix(.html)}", handler1) - app.Get("/api/{name:string suffix(.zip)}", handler2) - - return app -} - -func handler1(ctx iris.Context) { - reply(ctx) -} - -func handler2(ctx iris.Context) { - reply(ctx) -} - -func reply(ctx iris.Context) { - ctx.JSON(iris.Map{ - "handler": ctx.HandlerName(), - "params": ctx.Params().Store, - }) -} diff --git a/_examples/routing/dynamic-path/same-pattern-different-func/main_test.go b/_examples/routing/dynamic-path/same-pattern-different-func/main_test.go deleted file mode 100644 index 1e99dd6c2b..0000000000 --- a/_examples/routing/dynamic-path/same-pattern-different-func/main_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/core/memstore" - "github.com/kataras/iris/v12/httptest" -) - -func TestSameParameterTypeDifferentMacroFunctions(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - type resp struct { - Handler string `json:"handler"` - Params memstore.Store `json:"params"` - } - - var ( - expectedIndex = resp{ - Handler: "iris/_examples/routing/dynamic-path/same-pattern-different-func.handler1", - Params: nil, - } - expectedHTMLPage = resp{ - Handler: "iris/_examples/routing/dynamic-path/same-pattern-different-func.handler1", - Params: memstore.Store{ - {Key: "page", ValueRaw: "random.html"}, - }, - } - expectedZipName = resp{ - Handler: "iris/_examples/routing/dynamic-path/same-pattern-different-func.handler2", - Params: memstore.Store{ - {Key: "name", ValueRaw: "random.zip"}, - }, - } - ) - - e.GET("/").Expect().Status(httptest.StatusOK).JSON().Equal(expectedIndex) - e.GET("/api/random.html").Expect().Status(httptest.StatusOK).JSON().Equal(expectedHTMLPage) - e.GET("/api/random.zip").Expect().Status(httptest.StatusOK).JSON().Equal(expectedZipName) -} diff --git a/_examples/routing/dynamic-path/same-pattern-different-func/use-global/main.go b/_examples/routing/dynamic-path/same-pattern-different-func/use-global/main.go deleted file mode 100644 index 2f18f84fa8..0000000000 --- a/_examples/routing/dynamic-path/same-pattern-different-func/use-global/main.go +++ /dev/null @@ -1,42 +0,0 @@ -package main // #1552 - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := newApp() - app.Listen(":8080") -} - -func newApp() *iris.Application { - app := iris.New() - - app.UseGlobal(middleware("first")) - app.UseGlobal(middleware("second")) - app.DoneGlobal(onDone) - - app.Get("/{name prefix(one)}", handler("first route")) - app.Get("/{name prefix(two)}", handler("second route")) - app.Get("/{name prefix(three)}", handler("third route")) - - return app -} - -func middleware(str string) iris.Handler { - return func(ctx iris.Context) { - ctx.Writef("Called %s middleware\n", str) - ctx.Next() - } -} - -func handler(str string) iris.Handler { - return func(ctx iris.Context) { - ctx.Writef("%s\n", str) - ctx.Next() // or ignroe that and use app.SetRegisterRules. - } -} - -func onDone(ctx iris.Context) { - ctx.Writef("Called done: %s", ctx.Params().Get("name")) -} diff --git a/_examples/routing/dynamic-path/same-pattern-different-func/use-global/main_test.go b/_examples/routing/dynamic-path/same-pattern-different-func/use-global/main_test.go deleted file mode 100644 index 6cc2cc58e0..0000000000 --- a/_examples/routing/dynamic-path/same-pattern-different-func/use-global/main_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import ( - "fmt" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestSamePatternDifferentFuncUseGlobal(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - expectedResultFmt := "Called first middleware\nCalled second middleware\n%s\nCalled done: %s" - tests := map[string]string{ - "/one-num": "first route", - "/two-num": "second route", - "/three-num": "third route", - } - - for path, mainBody := range tests { - result := fmt.Sprintf(expectedResultFmt, mainBody, path[1:]) - e.GET(path).Expect().Status(httptest.StatusOK).Body().Equal(result) - } -} diff --git a/_examples/routing/hello-world/main.go b/_examples/routing/hello-world/main.go deleted file mode 100644 index 2b0ec41dd7..0000000000 --- a/_examples/routing/hello-world/main.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - - "github.com/kataras/iris/v12/middleware/logger" - "github.com/kataras/iris/v12/middleware/recover" -) - -func main() { - app := iris.New() - app.Logger().SetLevel("debug") - // Optionally, add two builtin handlers - // that can recover from any http-relative panics - // and log the requests to the terminal. - app.Use(recover.New()) - app.Use(logger.New()) - - // Method: GET - // Resource: http://localhost:8080 - app.Handle("GET", "/", func(ctx iris.Context) { - ctx.HTML("

Welcome

") - }) - - // same as app.Handle("GET", "/ping", [...]) - // Method: GET - // Resource: http://localhost:8080/ping - app.Get("/ping", func(ctx iris.Context) { - ctx.WriteString("pong") - }) - - // Method: GET - // Resource: http://localhost:8080/hello - app.Get("/hello", func(ctx iris.Context) { - ctx.JSON(iris.Map{"message": "Hello Iris!"}) - }) - - // http://localhost:8080 - // http://localhost:8080/ping - // http://localhost:8080/hello - app.Listen(":8080") -} diff --git a/_examples/routing/http-errors/main.go b/_examples/routing/http-errors/main.go deleted file mode 100644 index e3fdca01df..0000000000 --- a/_examples/routing/http-errors/main.go +++ /dev/null @@ -1,116 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - // Catch a specific error code. - app.OnErrorCode(iris.StatusInternalServerError, func(ctx iris.Context) { - ctx.HTML("Message: " + ctx.Values().GetString("message") + "") - }) - - // Catch all error codes [app.OnAnyErrorCode...] - - app.Get("/", func(ctx iris.Context) { - ctx.HTML(`Click here to pretend an HTTP error`) - }) - - app.Get("/my500", func(ctx iris.Context) { - ctx.Values().Set("message", "this is the error message") - ctx.StatusCode(500) - }) - - app.Get("/u/{firstname:alphabetical}", func(ctx iris.Context) { - ctx.Writef("Hello %s", ctx.Params().Get("firstname")) - }) - - // Read more at: https://github.com/kataras/iris/issues/1335 - app.Get("/product-problem", problemExample) - - app.Get("/product-error", func(ctx iris.Context) { - ctx.Writef("explain the error") - }) - - // http://localhost:8080 - // http://localhost:8080/my500 - // http://localhost:8080/u/gerasimos - // http://localhost:8080/product-problem - app.Listen(":8080") -} - -func newProductProblem(productName, detail string) iris.Problem { - return iris.NewProblem(). - // The type URI, if relative it automatically convert to absolute. - Type("/product-error"). - // The title, if empty then it gets it from the status code. - Title("Product validation problem"). - // Any optional details. - Detail(detail). - // The status error code, required. - Status(iris.StatusBadRequest). - // Any custom key-value pair. - Key("productName", productName) - // Optional cause of the problem, chain of Problems. - // Cause(iris.NewProblem().Type("/error").Title("cause of the problem").Status(400)) -} - -func problemExample(ctx iris.Context) { - /* - p := iris.NewProblem(). - Type("/validation-error"). - Title("Your request parameters didn't validate"). - Detail("Optional details about the error."). - Status(iris.StatusBadRequest). - Key("customField1", customValue1) - Key("customField2", customValue2) - ctx.Problem(p) - - // OR - ctx.Problem(iris.Problem{ - "type": "/validation-error", - "title": "Your request parameters didn't validate", - "detail": "Optional details about the error.", - "status": iris.StatusBadRequest, - "customField1": customValue1, - "customField2": customValue2, - }) - - // OR - */ - - // Response like JSON but with indent of " " and - // content type of "application/problem+json" - ctx.Problem(newProductProblem("product name", "problem error details"), iris.ProblemOptions{ - // Optional JSON renderer settings. - JSON: iris.JSON{ - Indent: " ", - }, - // OR - // Render as XML: - // - // RenderXML: true, - // XML: iris.XML{Indent: " "}, - // and ctx.StatusCode(200) to see the result on browser as a user. - // - // The below `RetryAfter` field sets the "Retry-After" response header. - // - // Can accept: - // time.Time for HTTP-Date, - // time.Duration, int64, float64, int for seconds - // or string for date or duration. - // Examples: - // time.Now().Add(5 * time.Minute), - // 300 * time.Second, - // "5m", - // - RetryAfter: 300, - // A function that, if specified, can dynamically set - // retry-after based on the request. Useful for ProblemOptions reusability. - // Overrides the RetryAfter field. - // - // RetryAfterFunc: func(iris.Context) interface{} { [...] } - }) -} diff --git a/_examples/routing/http-errors/reset-body/main.go b/_examples/routing/http-errors/reset-body/main.go deleted file mode 100644 index f00de10258..0000000000 --- a/_examples/routing/http-errors/reset-body/main.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/kataras/iris/v12" -) - -func main() { - app := newApp() - app.Listen(":8080") -} - -func newApp() *iris.Application { - app := iris.New() - app.Use(iris.Compression) - - app.OnAnyErrorCode(onErrorCode) - app.Get("/", handler) - - app.Configure(iris.WithResetOnFireErrorCode) - return app -} - -// This is the default error handler Iris uses for any error codes. -func onErrorCode(ctx iris.Context) { - if err := ctx.GetErr(); err != nil { - ctx.WriteString(err.Error()) - } else { - ctx.WriteString(iris.StatusText(ctx.GetStatusCode())) - } -} - -func handler(ctx iris.Context) { - ctx.Record() - - ctx.WriteString("This should NOT be written") - - // [....something bad happened after we "write"] - err := fmt.Errorf("custom error") - ctx.StopWithError(iris.StatusBadRequest, err) -} diff --git a/_examples/routing/http-errors/reset-body/main_test.go b/_examples/routing/http-errors/reset-body/main_test.go deleted file mode 100644 index 3218213e15..0000000000 --- a/_examples/routing/http-errors/reset-body/main_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestResetCompressionAndFireError(t *testing.T) { // #1569 - app := newApp() - - e := httptest.New(t, app) - e.GET("/").Expect().Status(httptest.StatusBadRequest).Body().Equal("custom error") -} diff --git a/_examples/routing/intelligence/main.go b/_examples/routing/intelligence/main.go deleted file mode 100644 index dbc2c631e6..0000000000 --- a/_examples/routing/intelligence/main.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - app.Get("/home", handler) - app.Get("/contact", handler) - app.Get("/contract", handler) - - // http://localhost:8080/home - // http://localhost:8080/hom - // - // http://localhost:8080/contact - // http://localhost:8080/cont - // - // http://localhost:8080/contract - // http://localhost:8080/contr - app.Listen(":8080", iris.WithPathIntelligence) -} - -func handler(ctx iris.Context) { - ctx.Writef("Path: %s", ctx.Path()) -} diff --git a/_examples/routing/intelligence/manual/main.go b/_examples/routing/intelligence/manual/main.go deleted file mode 100644 index b2bfb5a8b4..0000000000 --- a/_examples/routing/intelligence/manual/main.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - app.OnErrorCode(iris.StatusNotFound, notFound) - - // [register some routes...] - app.Get("/home", handler) - app.Get("/news", handler) - app.Get("/news/politics", handler) - app.Get("/user/profile", handler) - app.Get("/user", handler) - app.Get("/newspaper", handler) - app.Get("/user/{id}", handler) - - app.Listen(":8080") -} - -func notFound(ctx iris.Context) { - suggestPaths := ctx.FindClosest(3) - if len(suggestPaths) == 0 { - ctx.WriteString("404 not found") - return - } - - ctx.HTML("Did you mean?
    ") - for _, s := range suggestPaths { - ctx.HTML(`
  • %s
  • `, s, s) - } - ctx.HTML("
") -} - -func handler(ctx iris.Context) { - ctx.Writef("Path: %s", ctx.Path()) -} diff --git a/_examples/routing/macros/main.go b/_examples/routing/macros/main.go deleted file mode 100644 index 3a41b64605..0000000000 --- a/_examples/routing/macros/main.go +++ /dev/null @@ -1,76 +0,0 @@ -// Package main shows how you can register a custom parameter type and macro functions that belongs to it. -package main - -import ( - "fmt" - "reflect" - "sort" - "strings" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/hero" -) - -func main() { - app := iris.New() - app.Logger().SetLevel("debug") - - app.Macros().Register("slice", "", false, true, func(paramValue string) (interface{}, bool) { - return strings.Split(paramValue, "/"), true - }).RegisterFunc("contains", func(expectedItems []string) func(paramValue []string) bool { - sort.Strings(expectedItems) - return func(paramValue []string) bool { - if len(paramValue) != len(expectedItems) { - return false - } - - sort.Strings(paramValue) - for i := 0; i < len(paramValue); i++ { - if paramValue[i] != expectedItems[i] { - return false - } - } - - return true - } - }) - - // In order to use your new param type inside MVC controller's function input argument or a hero function input argument - // you have to tell the Iris what type it is, the `ValueRaw` of the parameter is the same type - // as you defined it above with the func(paramValue string) (interface{}, bool). - // The new value and its type(from string to your new custom type) it is stored only once now, - // you don't have to do any conversions for simple cases like this. - context.ParamResolvers[reflect.TypeOf([]string{})] = func(paramIndex int) interface{} { - return func(ctx iris.Context) []string { - // When you want to retrieve a parameter with a value type that it is not supported by-default, such as ctx.Params().GetInt - // then you can use the `GetEntry` or `GetEntryAt` and cast its underline `ValueRaw` to the desired type. - // The type should be the same as the macro's evaluator function (last argument on the Macros#Register) return value. - return ctx.Params().GetEntryAt(paramIndex).ValueRaw.([]string) - } - } - - /* - http://localhost:8080/test_slice_hero/myvaluei1/myavlue2 -> - myparam's value (a trailing path parameter type) is: []string{"myvalue1", "myavlue2"} - */ - app.Get("/test_slice_hero/{myparam:slice}", hero.Handler(func(myparam []string) string { - return fmt.Sprintf("myparam's value (a trailing path parameter type) is: %#v\n", myparam) - })) - - /* - http://localhost:8080/test_slice_contains/notcontains1/value2 -> - (404) Not Found - - http://localhost:8080/test_slice_contains/value1/value2 -> - myparam's value (a trailing path parameter type) is: []string{"value1", "value2"} - */ - app.Get("/test_slice_contains/{myparam:slice contains([value1,value2])}", func(ctx iris.Context) { - // When it is not a builtin function available to retrieve your value with the type you want, such as ctx.Params().GetInt - // then you can use the `GetEntry.ValueRaw` to get the real value, which is set-ed by your macro above. - myparam := ctx.Params().GetEntry("myparam").ValueRaw.([]string) - ctx.Writef("myparam's value (a trailing path parameter type) is: %#v\n", myparam) - }) - - app.Listen(":8080") -} diff --git a/_examples/routing/main.go b/_examples/routing/main.go deleted file mode 100644 index d7537ea4bf..0000000000 --- a/_examples/routing/main.go +++ /dev/null @@ -1,182 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -/* -Read: -"overview" -"basic" -"dynamic-path" -and "reverse" examples if you want to release iris' real power. -*/ - -const maxBodySize = 1 << 20 -const notFoundHTML = "

custom http error page

" - -func registerErrors(app *iris.Application) { - // set a custom 404 handler - app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) { - ctx.HTML(notFoundHTML) - }) -} - -func registerGamesRoutes(app *iris.Application) { - gamesMiddleware := func(ctx iris.Context) { - ctx.Next() - } - - // party is just a group of routes with the same prefix - // and middleware, i.e: "/games" and gamesMiddleware. - games := app.Party("/games", gamesMiddleware) - { // braces are optional of course, it's just a style of code - - // "GET" method - games.Get("/{gameID:uint64}/clans", h) - games.Get("/{gameID:uint64}/clans/clan/{clanPublicID:uint64}", h) - games.Get("/{gameID:uint64}/clans/search", h) - - // "PUT" method - games.Put("/{gameID:uint64}/players/{clanPublicID:uint64}", h) - games.Put("/{gameID:uint64}/clans/clan/{clanPublicID:uint64}", h) - // remember: "clanPublicID" should not be changed to other routes with the same prefix. - // "POST" method - games.Post("/{gameID:uint64}/clans", h) - games.Post("/{gameID:uint64}/players", h) - games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/leave", h) - games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/application", h) - games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/application/{action}", h) // {action} == {action:string} - games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/invitation", h) - games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/invitation/{action}", h) - games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/delete", h) - games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/promote", h) - games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/demote", h) - - gamesCh := games.Party("/challenge") - { - // games/challenge - gamesCh.Get("/", h) - - gamesChBeginner := gamesCh.Party("/beginner") - { - // games/challenge/beginner/start - gamesChBeginner.Get("/start", h) - levelBeginner := gamesChBeginner.Party("/level") - { - // games/challenge/beginner/level/first - levelBeginner.Get("/first", h) - } - } - - gamesChIntermediate := gamesCh.Party("/intermediate") - { - // games/challenge/intermediate - gamesChIntermediate.Get("/", h) - // games/challenge/intermediate/start - gamesChIntermediate.Get("/start", h) - } - } - - } -} - -func registerSubdomains(app *iris.Application) { - mysubdomain := app.Party("mysubdomain.") - // http://mysubdomain.myhost.com - mysubdomain.Get("/", h) - - willdcardSubdomain := app.Party("*.") - willdcardSubdomain.Get("/", h) - willdcardSubdomain.Party("/party").Get("/", h) -} - -func newApp() *iris.Application { - app := iris.New() - - registerErrors(app) - registerGamesRoutes(app) - registerSubdomains(app) - - app.Handle("GET", "/healthcheck", h) - - // "POST" method - // this handler reads raw body from the client/request - // and sends back the same body - // remember, we have limit to that body in order - // to protect ourselves from "over heating". - app.Post("/", iris.LimitRequestBodySize(maxBodySize), func(ctx iris.Context) { - // get request body - b, err := ctx.GetBody() - // if is larger then send a bad request status - if err != nil { - ctx.StatusCode(iris.StatusBadRequest) - ctx.Writef(err.Error()) - return - } - // send back the post body - ctx.Write(b) - }) - - app.HandleMany("POST PUT", "/postvalue", func(ctx iris.Context) { - name := ctx.PostValueDefault("name", "iris") - headervale := ctx.GetHeader("headername") - ctx.Writef("Hello %s | %s", name, headervale) - }) - - return app -} - -func h(ctx iris.Context) { - method := ctx.Method() // the http method requested a server's resource. - subdomain := ctx.Subdomain() // the subdomain, if any. - - // the request path (without scheme and host). - path := ctx.Path() - // how to get all parameters, if we don't know - // the names: - paramsLen := ctx.Params().Len() - - ctx.Params().Visit(func(name string, value string) { - ctx.Writef("%s = %s\n", name, value) - }) - ctx.Writef("Info\n\n") - ctx.Writef("Method: %s\nSubdomain: %s\nPath: %s\nParameters length: %d", method, subdomain, path, paramsLen) -} - -func main() { - app := newApp() - app.Logger().SetLevel("debug") - - /* - // GET - http://localhost:8080/healthcheck - http://localhost:8080/games/42/clans - http://localhost:8080/games/42/clans/clan/93 - http://localhost:8080/games/42/clans/search - http://mysubdomain.localhost:8080/ - - // PUT - http://localhost:8080/postvalue - http://localhost:8080/games/42/players/93 - http://localhost:8080/games/42/clans/clan/93 - - // POST - http://localhost:8080/ - http://localhost:8080/postvalue - http://localhost:8080/games/42/clans - http://localhost:8080/games/42/players - http://localhost:8080/games/42/clans/93/leave - http://localhost:8080/games/42/clans/93/memberships/application - http://localhost:8080/games/42/clans/93/memberships/application/anystring - http://localhost:8080/games/42/clans/93/memberships/invitation - http://localhost:8080/games/42/clans/93/memberships/invitation/anystring - http://localhost:8080/games/42/clans/93/memberships/delete - http://localhost:8080/games/42/clans/93/memberships/promote - http://localhost:8080/games/42/clans/93/memberships/demote - - // FIRE NOT FOUND - http://localhost:8080/coudlntfound - */ - app.Listen(":8080") -} diff --git a/_examples/routing/main_test.go b/_examples/routing/main_test.go deleted file mode 100644 index 93db302d02..0000000000 --- a/_examples/routing/main_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package main - -import ( - "strconv" - "strings" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func calculatePathAndResponse(method, subdomain, path string, paramKeyValue ...string) (string, string) { - paramsLen := 0 - - if l := len(paramKeyValue); l >= 2 { - paramsLen = len(paramKeyValue) / 2 - } - - paramsInfo := "" - if paramsLen > 0 { - for i := 0; i < len(paramKeyValue); i++ { - paramKey := paramKeyValue[i] - i++ - if i >= len(paramKeyValue) { - panic("paramKeyValue should be align with path parameters {} and must be placed in order") - } - - paramValue := paramKeyValue[i] - paramsInfo += paramKey + " = " + paramValue + "\n" - - beginParam := strings.IndexByte(path, '{') - endParam := strings.IndexByte(path, '}') - if beginParam == -1 || endParam == -1 { - panic("something wrong with parameters, please define them in order") - } - - path = path[:beginParam] + paramValue + path[endParam+1:] - } - } - - return path, paramsInfo + `Info - -Method: ` + method + ` -Subdomain: ` + subdomain + ` -Path: ` + path + ` -Parameters length: ` + strconv.Itoa(paramsLen) -} - -type troute struct { - method, subdomain, path string - status int - expectedBody string - contentType string -} - -func newTroute(method, subdomain, path string, status int, paramKeyValue ...string) troute { - finalPath, expectedBody := calculatePathAndResponse(method, subdomain, path, paramKeyValue...) - contentType := "text/plain; charset=UTF-8" - - if status == httptest.StatusNotFound { - expectedBody = notFoundHTML - contentType = "text/html; charset=UTF-8" - } - - return troute{ - contentType: contentType, - method: method, - subdomain: subdomain, - path: finalPath, - status: status, - expectedBody: expectedBody, - } -} - -func TestRouting(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - tests := []troute{ - // GET - newTroute("GET", "", "/healthcheck", httptest.StatusOK), - newTroute("GET", "", "/games/{gameID}/clans", httptest.StatusOK, "gameID", "42"), - newTroute("GET", "", "/games/{gameID}/clans/clan/{clanPublicID}", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"), - newTroute("GET", "", "/games/{gameID}/clans/search", httptest.StatusOK, "gameID", "42"), - newTroute("GET", "", "/games/challenge", httptest.StatusOK), - newTroute("GET", "", "/games/challenge/beginner/start", httptest.StatusOK), - newTroute("GET", "", "/games/challenge/beginner/level/first", httptest.StatusOK), - newTroute("GET", "", "/games/challenge/intermediate", httptest.StatusOK), - newTroute("GET", "", "/games/challenge/intermediate/start", httptest.StatusOK), - newTroute("GET", "mysubdomain", "/", httptest.StatusOK), - newTroute("GET", "mywildcardsubdomain", "/", httptest.StatusOK), - newTroute("GET", "mywildcardsubdomain", "/party", httptest.StatusOK), - // PUT - newTroute("PUT", "", "/games/{gameID}/players/{clanPublicID}", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"), - newTroute("PUT", "", "/games/{gameID}/clans/clan/{clanPublicID}", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"), - // POST - newTroute("POST", "", "/games/{gameID}/clans", httptest.StatusOK, "gameID", "42"), - newTroute("POST", "", "/games/{gameID}/players", httptest.StatusOK, "gameID", "42"), - newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/leave", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"), - newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/memberships/application", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"), - newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/memberships/application/{action}", httptest.StatusOK, "gameID", "42", "clanPublicID", "93", "action", "somethinghere"), - newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/memberships/invitation", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"), - newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/memberships/invitation/{action}", httptest.StatusOK, "gameID", "42", "clanPublicID", "93", "action", "somethinghere"), - newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/memberships/delete", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"), - newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/memberships/promote", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"), - newTroute("POST", "", "/games/{gameID}/clans/{clanPublicID}/memberships/demote", httptest.StatusOK, "gameID", "42", "clanPublicID", "93"), - // POST: / will be tested alone - // custom not found - newTroute("GET", "", "/notfound", httptest.StatusNotFound), - newTroute("POST", "", "/notfound2", httptest.StatusNotFound), - newTroute("PUT", "", "/notfound3", httptest.StatusNotFound), - newTroute("GET", "mysubdomain", "/notfound42", httptest.StatusNotFound), - } - - for _, tt := range tests { - et := e.Request(tt.method, tt.path) - if tt.subdomain != "" { - et.WithURL("http://" + tt.subdomain + ".localhost:8080") - } - et.Expect().Status(tt.status).Body().Equal(tt.expectedBody) - } - - // test POST "/" limit data and post data return - - // test with small body - e.POST("/").WithBytes([]byte("ok")).Expect().Status(httptest.StatusOK).Body().Equal("ok") - // test with equal to max body size limit - bsent := make([]byte, maxBodySize, maxBodySize) - e.POST("/").WithBytes(bsent).Expect().Status(httptest.StatusOK).Body().Length().Equal(len(bsent)) - // test with larger body sent and wait for the custom response - largerBSent := make([]byte, maxBodySize+1, maxBodySize+1) - e.POST("/").WithBytes(largerBSent).Expect().Status(httptest.StatusBadRequest).Body().Equal("http: request body too large") - - // test the post value (both post and put) and headers. - e.PUT("/postvalue").WithFormField("name", "test_put"). - WithHeader("headername", "headervalue_put").Expect(). - Status(httptest.StatusOK).Body().Equal("Hello test_put | headervalue_put") - - e.POST("/postvalue").WithFormField("name", "test_post"). - WithHeader("headername", "headervalue_post").Expect(). - Status(httptest.StatusOK).Body().Equal("Hello test_post | headervalue_post") -} diff --git a/_examples/routing/overview-2/main.go b/_examples/routing/overview-2/main.go deleted file mode 100644 index 83b222cf40..0000000000 --- a/_examples/routing/overview-2/main.go +++ /dev/null @@ -1,126 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -// User is just a bindable object structure. -type User struct { - Username string `json:"username"` - Firstname string `json:"firstname"` - Lastname string `json:"lastname"` - City string `json:"city"` - Age int `json:"age"` -} - -func main() { - app := iris.New() - app.Logger().SetLevel("debug") - // app.Logger().SetLevel("disable") to disable the logger. - - // Define templates using the std html/template engine. - // Parse and load all files inside "./views" folder with ".html" file extension. - // Reload the templates on each request (development mode). - app.RegisterView(iris.HTML("./views", ".html").Reload(true)) - - // Register custom handler for specific http errors. - app.OnErrorCode(iris.StatusInternalServerError, func(ctx iris.Context) { - // .Values are used to communicate between handlers, middleware. - errMessage := ctx.Values().GetString("error") - if errMessage != "" { - ctx.Writef("Internal server error: %s", errMessage) - return - } - - ctx.Writef("(Unexpected) internal server error") - }) - - app.Use(func(ctx iris.Context) { - ctx.Application().Logger().Infof("Begin request for path: %s", ctx.Path()) - ctx.Next() - }) - // app.Done(func(ctx iris.Context) {]}) - - // POST: scheme://mysubdomain.$domain.com/decode - app.Subdomain("mysubdomain.").Post("/decode", func(ctx iris.Context) {}) - // Method POST: http://localhost:8080/decode - app.Post("/decode", func(ctx iris.Context) { - var user User - ctx.ReadJSON(&user) - ctx.Writef("%s %s is %d years old and comes from %s", user.Firstname, user.Lastname, user.Age, user.City) - }) - - // Method GET: http://localhost:8080/encode - app.Get("/encode", func(ctx iris.Context) { - doe := User{ - Username: "Johndoe", - Firstname: "John", - Lastname: "Doe", - City: "Neither FBI knows!!!", - Age: 25, - } - - ctx.JSON(doe) - }) - - // Method GET: http://localhost:8080/profile/anytypeofstring - app.Get("/profile/{username:string}", profileByUsername) - - usersRoutes := app.Party("/users", logThisMiddleware) - { - // Method GET: http://localhost:8080/users/42 - usersRoutes.Get("/{id:int min(1)}", getUserByID) - // Method POST: http://localhost:8080/users/create - usersRoutes.Post("/create", createUser) - } - - app.Get("/", func(ctx iris.Context) { - ctx.HTML("
    ") - for _, link := range []string{"/encode", "/profile/username", "/users/42"} { - ctx.HTML(`
  • %s
  • `, link, link) - } - ctx.HTML("
") - }) - - // Listen for incoming HTTP/1.x & HTTP/2 clients on localhost port 8080. - app.Listen(":8080", iris.WithCharset("utf-8")) -} - -func logThisMiddleware(ctx iris.Context) { - ctx.Application().Logger().Infof("Path: %s | IP: %s", ctx.Path(), ctx.RemoteAddr()) - - // .Next is required to move forward to the chain of handlers, - // if missing then it stops the execution at this handler. - ctx.Next() -} - -func profileByUsername(ctx iris.Context) { - // .Params are used to get dynamic path parameters. - username := ctx.Params().Get("username") - ctx.ViewData("Username", username) - // renders "./views/user/profile.html" - // with {{ .Username }} equals to the username dynamic path parameter. - ctx.View("user/profile.html") -} - -func getUserByID(ctx iris.Context) { - userID := ctx.Params().Get("id") // Or convert directly using: .Values().GetInt/GetInt64 etc... - // your own db fetch here instead of user :=... - user := User{Username: "username" + userID} - - ctx.XML(user) -} - -func createUser(ctx iris.Context) { - var user User - err := ctx.ReadForm(&user) - if err != nil { - ctx.Values().Set("error", "creating user, read and parse form failed. "+err.Error()) - ctx.StatusCode(iris.StatusInternalServerError) - return - } - // renders "./views/user/create_verification.html" - // with {{ . }} equals to the User object, i.e {{ .Username }} , {{ .Firstname}} etc... - ctx.ViewData("", user) - ctx.View("user/create_verification.html") -} diff --git a/_examples/routing/overview-2/views/user/create_verification.html b/_examples/routing/overview-2/views/user/create_verification.html deleted file mode 100644 index a19813808b..0000000000 --- a/_examples/routing/overview-2/views/user/create_verification.html +++ /dev/null @@ -1,22 +0,0 @@ - - Create verification - -

Create Verification

- - - - - - - - - - - - - - - -
UsernameFirstnameLastnameCityAge
{{ .Username }}{{ .Firstname }}{{ .Lastname }}{{ .City }}{{ .Age }}
- - diff --git a/_examples/routing/overview-2/views/user/profile.html b/_examples/routing/overview-2/views/user/profile.html deleted file mode 100644 index 7b3ceebc1d..0000000000 --- a/_examples/routing/overview-2/views/user/profile.html +++ /dev/null @@ -1,7 +0,0 @@ - - Profile page - -

Profile

- {{ .Username }} - - diff --git a/_examples/routing/overview/main.go b/_examples/routing/overview/main.go deleted file mode 100644 index 2e327a2aa8..0000000000 --- a/_examples/routing/overview/main.go +++ /dev/null @@ -1,192 +0,0 @@ -package main - -import ( - "os" - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/logger" -) - -func main() { - app := iris.New() - // Set Logger level to "debug", - // see your terminal and the created file. - app.Logger().SetLevel("debug") - - // Write logs to a file too. - f := newLogFile() - defer f.Close() - app.Logger().AddOutput(f) - - // Register a request logger middleware to the application. - app.Use(logger.New()) - - // GET: http://localhost:8080 - app.Get("/", info) - - // GET: http://localhost:8080/profile/anyusername - // - // Want to use a custom regex expression instead? - // Easy: app.Get("/profile/{username:string regexp(^[a-zA-Z ]+$)}") - app.Get("/profile/{username:string}", info) - - // If parameter type is missing then it's string which accepts anything, - // i.e: /{paramname} it's exactly the same as /{paramname:string}. - // The below is exactly the same as - // {username:string} - // - // GET: http://localhost:8080/profile/anyusername/backups/any/number/of/paths/here - app.Get("/profile/{username}/backups/{filepath:path}", info) - - // Favicon - - // GET: http://localhost:8080/favicon.ico - app.Favicon("./public/images/favicon.ico") - - // Static assets - - // GET: http://localhost:8080/assets/css/main.css - // maps to ./public/assets/css/main.css file at system location. - app.HandleDir("/assets", iris.Dir("./public/assets")) - - /* OR - - // GET: http://localhost:8080/css/main.css - // maps to ./public/assets/css/main.css file at system location. - app.HandleDir("/css", iris.Dir("./public/assets/css")) - - // GET: http://localhost:8080/css/bootstrap.min.css - // maps to ./public/assets/css/bootstrap.min.css file at system location. - app.HandleDir("/css", iris.Dir("./public/assets/css")) - - */ - - // Grouping - - usersRoutes := app.Party("/users") - // GET: http://localhost:8080/users/help - usersRoutes.Get("/help", func(ctx iris.Context) { - ctx.Writef("GET / -- fetch all users\n") - ctx.Writef("GET /$ID -- fetch a user by id\n") - ctx.Writef("POST / -- create new user\n") - ctx.Writef("PUT /$ID -- update an existing user\n") - ctx.Writef("DELETE /$ID -- delete an existing user\n") - }) - - // GET: http://localhost:8080/users - usersRoutes.Get("/", func(ctx iris.Context) { - ctx.Writef("get all users") - }) - - // GET: http://localhost:8080/users/42 - // **/users/42 and /users/help works after iris version 7.0.5** - usersRoutes.Get("/{id:uint64}", func(ctx iris.Context) { - id, _ := ctx.Params().GetUint64("id") - ctx.Writef("get user by id: %d", id) - }) - - // POST: http://localhost:8080/users - usersRoutes.Post("/", func(ctx iris.Context) { - username, password := ctx.PostValue("username"), ctx.PostValue("password") - ctx.Writef("create user for username= %s and password= %s", username, password) - }) - - // PUT: http://localhost:8080/users - usersRoutes.Put("/{id:uint64}", func(ctx iris.Context) { - id, _ := ctx.Params().GetUint64("id") // or .Get to get its string represatantion. - username := ctx.PostValue("username") - ctx.Writef("update user for id= %d and new username= %s", id, username) - }) - - // DELETE: http://localhost:8080/users/42 - usersRoutes.Delete("/{id:uint64}", func(ctx iris.Context) { - id, _ := ctx.Params().GetUint64("id") - ctx.Writef("delete user by id: %d", id) - }).Describe("deletes a user") - - // Subdomains, depends on the host, you have to edit the hosts or nginx/caddy's configuration if you use them. - // - // See more subdomains examples at _examples/routing/subdomains folder. - adminRoutes := app.Party("admin.") - - // GET: http://admin.localhost:8080 - adminRoutes.Get("/", info) - // GET: http://admin.localhost:8080/settings - adminRoutes.Get("/settings", info) - - // Wildcard/dynamic subdomain - dynamicSubdomainRoutes := app.Party("*.") - - // GET: http://any_thing_here.localhost:8080 - dynamicSubdomainRoutes.Get("/", info) - - app.Delete("/something", func(ctx iris.Context) { - name := ctx.URLParam("name") - ctx.Writef(name) - }) - - app.None("/secret", privateHandler) - app.Get("/public", execPrivateHandler) - - // GET: http://localhost:8080/ - // GET: http://localhost:8080/profile/anyusername - // GET: http://localhost:8080/profile/anyusername/backups/any/number/of/paths/here - - // GET: http://localhost:8080/users/help - // GET: http://localhost:8080/users - // GET: http://localhost:8080/users/42 - // POST: http://localhost:8080/users - // PUT: http://localhost:8080/users - // DELETE: http://localhost:8080/users/42 - // DELETE: http://localhost:8080/something?name=iris - - // GET: http://admin.localhost:8080 - // GET: http://admin.localhost:8080/settings - // GET: http://any_thing_here.localhost:8080 - app.Listen(":8080") -} - -func privateHandler(ctx iris.Context) { - ctx.WriteString(`This can only be executed programmatically through server's another route: -ctx.Exec(iris.MethodNone, "/secret")`) -} - -func execPrivateHandler(ctx iris.Context) { - ctx.Exec(iris.MethodNone, "/secret") -} - -func info(ctx iris.Context) { - method := ctx.Method() // the http method requested a server's resource. - subdomain := ctx.Subdomain() // the subdomain, if any. - - // the request path (without scheme and host). - path := ctx.Path() - // how to get all parameters, if we don't know - // the names: - paramsLen := ctx.Params().Len() - - ctx.Params().Visit(func(name string, value string) { - ctx.Writef("%s = %s\n", name, value) - }) - ctx.Writef("\nInfo\n\n") - ctx.Writef("Method: %s\nSubdomain: %s\nPath: %s\nParameters length: %d", method, subdomain, path, paramsLen) -} - -// get a filename based on the date, file logs works that way the most times -// but these are just a sugar. -func todayFilename() string { - today := time.Now().Format("Jan 02 2006") - return today + ".txt" -} - -func newLogFile() *os.File { - filename := todayFilename() - // open an output file, this will append to the today's file if server restarted. - f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) - if err != nil { - panic(err) - } - - return f -} diff --git a/_examples/routing/overview/public/assets/css/main.css b/_examples/routing/overview/public/assets/css/main.css deleted file mode 100644 index 7db3df1d78..0000000000 --- a/_examples/routing/overview/public/assets/css/main.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: black; -} diff --git a/_examples/routing/overview/public/images/favicon.ico b/_examples/routing/overview/public/images/favicon.ico deleted file mode 100644 index c370da518e..0000000000 Binary files a/_examples/routing/overview/public/images/favicon.ico and /dev/null differ diff --git a/_examples/routing/reverse/main.go b/_examples/routing/reverse/main.go deleted file mode 100644 index e6ae74136c..0000000000 --- a/_examples/routing/reverse/main.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/core/router" -) - -func main() { - app := iris.New() - // need for manually reverse routing when needed outside of view engine. - // you normally don't need it because of the {{ urlpath "routename" "path" "values" "here"}} - rv := router.NewRoutePathReverser(app) - - myroute := app.Get("/anything/{anythingparameter:path}", func(ctx iris.Context) { - paramValue := ctx.Params().Get("anythingparameter") - ctx.Writef("The path after /anything is: %s", paramValue) - }) - - myroute.Name = "myroute" - - // useful for links, although iris' view engine has the {{ urlpath "routename" "path values"}} already. - app.Get("/reverse_myroute", func(ctx iris.Context) { - myrouteRequestPath := rv.Path(myroute.Name, "any/path") - ctx.HTML("Should be /anything/any/path: " + myrouteRequestPath) - }) - - // execute a route, similar to redirect but without redirect :) - app.Get("/execute_myroute", func(ctx iris.Context) { - ctx.Exec("GET", "/anything/any/path") // like it was called by the client. - }) - - // http://localhost:8080/reverse_myroute - // http://localhost:8080/execute_myroute - // http://localhost:8080/anything/any/path/here - // - // See view/template_html_4 example for more reverse routing examples - // using the reverse router component and the {{url}} and {{urlpath}} template functions. - app.Listen(":8080") -} diff --git a/_examples/routing/route-handlers-execution-rules/main.go b/_examples/routing/route-handlers-execution-rules/main.go deleted file mode 100644 index 5b503b7e5f..0000000000 --- a/_examples/routing/route-handlers-execution-rules/main.go +++ /dev/null @@ -1,61 +0,0 @@ -/*Package main is a simple example of the behavior change of the execution flow of the handlers, -normally we need the `ctx.Next()` to call the next handler in a route's handler chain, -but with the `ExecutionRules` we can change this default behavior. -Please read below before continue. - -The `Party#SetExecutionRules` alters the execution flow of the route handlers. - -For example, if for some reason the desired result is the (done or all) handlers -to be executed no matter what, even if no `ctx.Next()` is called in the previous handlers: - -app.SetExecutionRules(iris.ExecutionRules { - Begin: iris.ExecutionOptions{Force: true}, # begin handlers(.Use) - Main: iris.ExecutionOptions{Force: true}, # main handler (.Handle/Get...) - Done: iris.ExecutionOptions{Force: true}, # done handlers (.Done) -}) - -Note that if `true` then the only remained way to "break" the handler chain -is by calling the `ctx.StopExecution()` (now that `ctx.Next()` doesn't even matter). - -These rules are per-party, so if a `Party` creates a child one then -the same rules will be applied to that as well. - -Reset of these rules to their defaults (before `Party#Handle`) can be done -with `Party#SetExecutionRules(iris.ExecutionRules{})`. - -*/ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - - app.SetExecutionRules(iris.ExecutionRules{ - // * From `Use[all]` to `Handle[last]` future route handlers, - // execute all (even if `ctx.Next()` is missing): - // Begin: true, - // - // * All `Handle` future route handlers, execute all: - // Main: true, - // - // * From `Handle[last]` to `Done[last]` future route handlers, execute all: - Done: iris.ExecutionOptions{Force: true}, - }) - app.Done(doneHandler) - - app.Get("/", mainHandler) - - // http://localhost:8080 - app.Listen(":8080") -} - -func mainHandler(ctx iris.Context) { - ctx.WriteString("From Main Handler\n") - // ctx.Next() is not required now that we have declared - // Done: iris.ExecutionOptions{Force: true}. -} - -func doneHandler(ctx iris.Context) { - ctx.WriteString("From Done Handler\n") -} diff --git a/_examples/routing/route-register-rule/main.go b/_examples/routing/route-register-rule/main.go deleted file mode 100644 index 31f5cb0826..0000000000 --- a/_examples/routing/route-register-rule/main.go +++ /dev/null @@ -1,45 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := newApp() - // Navigate through https://github.com/kataras/iris/issues/1448 for details. - // - // GET: http://localhost:8080 - // POST, PUT, DELETE, CONNECT, HEAD, PATCH, OPTIONS, TRACE : http://localhost:8080 - app.Listen(":8080") -} - -func newApp() *iris.Application { - app := iris.New() - // Skip and do NOT override existing regitered route, continue normally. - // Applies to a Party and its children, in this case the whole application's routes. - app.SetRegisterRule(iris.RouteSkip) - - /* Read also: - // The default behavior, will override the getHandler to anyHandler on `app.Any` call. - app.SetRegistRule(iris.RouteOverride) - - // Stops the execution and fires an error before server boot. - app.SetRegisterRule(iris.RouteError) - - // If ctx.StopExecution or StopWithXXX then the next route will be executed - // (see mvc/authenticated-controller example too). - app.SetRegisterRule(iris.RouteOverlap) - */ - - app.Get("/", getHandler) - // app.Any does NOT override the previous GET route because of `iris.RouteSkip` rule. - app.Any("/", anyHandler) - - return app -} - -func getHandler(ctx iris.Context) { - ctx.Writef("From GET: %s", ctx.GetCurrentRoute().MainHandlerName()) -} - -func anyHandler(ctx iris.Context) { - ctx.Writef("From %s: %s", ctx.Method(), ctx.GetCurrentRoute().MainHandlerName()) -} diff --git a/_examples/routing/route-register-rule/main_test.go b/_examples/routing/route-register-rule/main_test.go deleted file mode 100644 index 9ac5ed9f65..0000000000 --- a/_examples/routing/route-register-rule/main_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12/core/router" - "github.com/kataras/iris/v12/httptest" -) - -func TestRouteRegisterRuleExample(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - for _, method := range router.AllMethods { - tt := e.Request(method, "/").Expect().Status(httptest.StatusOK).Body() - if method == "GET" { - tt.Equal("From GET: iris/_examples/routing/route-register-rule.getHandler") - } else { - tt.Equal("From " + method + ": iris/_examples/routing/route-register-rule.anyHandler") - } - } -} diff --git a/_examples/routing/route-state/main.go b/_examples/routing/route-state/main.go deleted file mode 100644 index 6c362c1159..0000000000 --- a/_examples/routing/route-state/main.go +++ /dev/null @@ -1,45 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - none := app.None("/invisible/{username}", func(ctx iris.Context) { - ctx.Writef("Hello %s with method: %s", ctx.Params().Get("username"), ctx.Method()) - - if from := ctx.Values().GetString("from"); from != "" { - ctx.Writef("\nI see that you're coming from %s", from) - } - }) - - app.Get("/change", func(ctx iris.Context) { - if none.IsOnline() { - none.Method = iris.MethodNone - } else { - none.Method = iris.MethodGet - } - - // refresh re-builds the router at serve-time in order to be notified for its new routes. - app.RefreshRouter() - }) - - app.Get("/execute", func(ctx iris.Context) { - if !none.IsOnline() { - ctx.Values().Set("from", "/execute with offline access") - ctx.Exec("NONE", "/invisible/iris") - return - } - - // same as navigating to "http://localhost:8080/invisible/iris" when /change has being invoked and route state changed - // from "offline" to "online" - ctx.Values().Set("from", "/execute") // values and session can be shared when calling Exec from a "foreign" context. - // ctx.Exec("NONE", "/invisible/iris") - // or after "/change": - ctx.Exec("GET", "/invisible/iris") - }) - - app.Listen(":8080") -} diff --git a/_examples/routing/sitemap/main.go b/_examples/routing/sitemap/main.go deleted file mode 100644 index 31b2a9f786..0000000000 --- a/_examples/routing/sitemap/main.go +++ /dev/null @@ -1,38 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12" -) - -const startURL = "http://localhost:8080" - -func main() { - app := newApp() - - // http://localhost:8080/sitemap.xml - // Lists only online GET static routes. - // - // Reference: https://www.sitemaps.org/protocol.html - app.Listen(":8080", iris.WithSitemap(startURL)) -} - -func newApp() *iris.Application { - app := iris.New() - app.Logger().SetLevel("debug") - - lastModified, _ := time.Parse("2006-01-02T15:04:05-07:00", "2019-12-13T21:50:33+02:00") - app.Get("/home", handler).SetLastMod(lastModified).SetChangeFreq("hourly").SetPriority(1) - app.Get("/articles", handler).SetChangeFreq("daily") - app.Get("/path1", handler) - app.Get("/path2", handler) - - app.Post("/this-should-not-be-listed", handler) - app.Get("/this/{myparam}/should/not/be/listed", handler) - app.Get("/this-should-not-be-listed-offline", handler).SetStatusOffline() - - return app -} - -func handler(ctx iris.Context) { ctx.WriteString(ctx.Path()) } diff --git a/_examples/routing/sitemap/main_test.go b/_examples/routing/sitemap/main_test.go deleted file mode 100644 index 432fc9a5b5..0000000000 --- a/_examples/routing/sitemap/main_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/httptest" -) - -func TestSitemap(t *testing.T) { - const expectedFullSitemapXML = `http://localhost:8080/home2019-12-13T21:50:33+02:00hourly1http://localhost:8080/articlesdailyhttp://localhost:8080/path1http://localhost:8080/path2` - - app := newApp() - app.Configure(iris.WithSitemap(startURL)) - - e := httptest.New(t, app) - e.GET("/sitemap.xml").Expect().Status(httptest.StatusOK).Body().Equal(expectedFullSitemapXML) -} diff --git a/_examples/routing/subdomains/multi/hosts b/_examples/routing/subdomains/multi/hosts deleted file mode 100644 index b862e00eaf..0000000000 --- a/_examples/routing/subdomains/multi/hosts +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) 1993-2009 Microsoft Corp. -# -# This is a sample HOSTS file used by Microsoft TCP/IP for Windows. -# -# This file contains the mappings of IP addresses to host names. Each -# entry should be kept on an individual line. The IP address should -# be placed in the first column followed by the corresponding host name. -# The IP address and the host name should be separated by at least one -# space. -# -# Additionally, comments (such as these) may be inserted on individual -# lines or following the machine name denoted by a '#' symbol. -# -# For example: -# -# 102.54.94.97 rhino.acme.com # source server -# 38.25.63.10 x.acme.com # x client host - -# localhost name resolution is handled within DNS itself. -127.0.0.1 localhost -::1 localhost -#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know - -127.0.0.1 domain.local -127.0.0.1 system.domain.local -127.0.0.1 dashboard.domain.local - -#-END iris- diff --git a/_examples/routing/subdomains/multi/main.go b/_examples/routing/subdomains/multi/main.go deleted file mode 100644 index aa16296f1d..0000000000 --- a/_examples/routing/subdomains/multi/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - /* - * Setup static files - */ - - app.HandleDir("/assets", iris.Dir("./public/assets")) - app.HandleDir("/upload_resources", iris.Dir("./public/upload_resources")) - - dashboard := app.Party("dashboard.") - { - dashboard.Get("/", func(ctx iris.Context) { - ctx.Writef("HEY FROM dashboard") - }) - } - system := app.Party("system.") - { - system.Get("/", func(ctx iris.Context) { - ctx.Writef("HEY FROM system") - }) - } - - app.Get("/", func(ctx iris.Context) { - ctx.Writef("HEY FROM frontend /") - }) - // http://domain.local:80 - // http://dashboard.local - // http://system.local - // Make sure you prepend the "http" in your browser - // because .local is a virtual domain we think to show case you - // that you can declare any syntactical correct name as a subdomain in iris. - app.Listen("domain.local:80") // for beginners: look ../hosts file -} diff --git a/_examples/routing/subdomains/multi/public/assets/images/test.ico b/_examples/routing/subdomains/multi/public/assets/images/test.ico deleted file mode 100644 index 961ef6dac8..0000000000 Binary files a/_examples/routing/subdomains/multi/public/assets/images/test.ico and /dev/null differ diff --git a/_examples/routing/subdomains/multi/public/upload_resources/favicon.ico b/_examples/routing/subdomains/multi/public/upload_resources/favicon.ico deleted file mode 100644 index c370da518e..0000000000 Binary files a/_examples/routing/subdomains/multi/public/upload_resources/favicon.ico and /dev/null differ diff --git a/_examples/routing/subdomains/redirect/hosts b/_examples/routing/subdomains/redirect/hosts deleted file mode 100644 index c4c064782b..0000000000 --- a/_examples/routing/subdomains/redirect/hosts +++ /dev/null @@ -1,4 +0,0 @@ -127.0.0.1 mydomain.com -127.0.0.1 www.mydomain.com - -# Windows: Drive:/Windows/system32/drivers/etc/hosts, on Linux: /etc/hosts \ No newline at end of file diff --git a/_examples/routing/subdomains/redirect/main.go b/_examples/routing/subdomains/redirect/main.go deleted file mode 100644 index 51562a99a3..0000000000 --- a/_examples/routing/subdomains/redirect/main.go +++ /dev/null @@ -1,73 +0,0 @@ -// Package main shows how to register a simple 'www' subdomain, -// using the `app.WWW` method, which will register a router wrapper which will -// redirect all 'mydomain.com' requests to 'www.mydomain.com'. -// Check the 'hosts' file to see how to test the 'mydomain.com' on your local machine. -package main - -import "github.com/kataras/iris/v12" - -const addr = "mydomain.com:80" - -func main() { - app := newApp() - - // http(s)://mydomain.com, will be redirect to http(s)://www.mydomain.com. - // The `www` variable is the `app.Subdomain("www")`. - // - // app.WWW() wraps the router so it can redirect all incoming requests - // that comes from 'http(s)://mydomain.com/%path%' (www is missing) - // to `http(s)://www.mydomain.com/%path%`. - // - // Try: - // http://mydomain.com -> http://www.mydomain.com - // http://mydomain.com/users -> http://www.mydomain.com/users - // http://mydomain.com/users/login -> http://www.mydomain.com/users/login - app.Listen(addr) -} - -func newApp() *iris.Application { - app := iris.New() - app.Get("/", func(ctx iris.Context) { - ctx.Writef("This will never be executed.") - }) - - www := app.Subdomain("www") // <- same as app.Party("www.") - www.Get("/", index) - - // www is an `iris.Party`, use it like you already know, like grouping routes. - www.PartyFunc("/users", func(p iris.Party) { // <- same as www.Party("/users").Get(...) - p.Get("/", usersIndex) - p.Get("/login", getLogin) - }) - - // redirects mydomain.com/%anypath% to www.mydomain.com/%anypath%. - // First argument is the 'from' and second is the 'to/target'. - app.SubdomainRedirect(app, www) - - // SubdomainRedirect works for multi-level subdomains as well: - // subsub := www.Subdomain("subsub") // subsub.www.mydomain.com - // subsub.Get("/", func(ctx iris.Context) { ctx.Writef("subdomain is: " + ctx.Subdomain()) }) - // app.SubdomainRedirect(subsub, www) - // - // If you need to redirect any subdomain to 'www' then: - // app.SubdomainRedirect(app.WildcardSubdomain(), www) - // If you need to redirect from a subdomain to the root domain then: - // app.SubdomainRedirect(app.Subdomain("mysubdomain"), app) - // - // Note that app.Party("mysubdomain.") and app.Subdomain("mysubdomain") - // is the same exactly thing, the difference is that the second can omit the last dot('.'). - - return app -} - -func index(ctx iris.Context) { - ctx.Writef("This is the www.mydomain.com endpoint.") -} - -func usersIndex(ctx iris.Context) { - ctx.Writef("This is the www.mydomain.com/users endpoint.") -} - -func getLogin(ctx iris.Context) { - ctx.Writef("This is the www.mydomain.com/users/login endpoint.") -} diff --git a/_examples/routing/subdomains/redirect/main_test.go b/_examples/routing/subdomains/redirect/main_test.go deleted file mode 100644 index d790c8785a..0000000000 --- a/_examples/routing/subdomains/redirect/main_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "fmt" - "strings" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestSubdomainRedirectWWW(t *testing.T) { - app := newApp() - root := strings.TrimSuffix(addr, ":80") - - e := httptest.New(t, app) - - tests := []struct { - path string - response string - }{ - {"/", fmt.Sprintf("This is the www.%s endpoint.", root)}, - {"/users", fmt.Sprintf("This is the www.%s/users endpoint.", root)}, - {"/users/login", fmt.Sprintf("This is the www.%s/users/login endpoint.", root)}, - } - - for _, test := range tests { - e.GET(test.path).Expect().Status(httptest.StatusOK).Body().Equal(test.response) - } -} diff --git a/_examples/routing/subdomains/single/hosts b/_examples/routing/subdomains/single/hosts deleted file mode 100644 index e7f394a159..0000000000 --- a/_examples/routing/subdomains/single/hosts +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 1993-2009 Microsoft Corp. -# -# This is a sample HOSTS file used by Microsoft TCP/IP for Windows. -# -# This file contains the mappings of IP addresses to host names. Each -# entry should be kept on an individual line. The IP address should -# be placed in the first column followed by the corresponding host name. -# The IP address and the host name should be separated by at least one -# space. -# -# Additionally, comments (such as these) may be inserted on individual -# lines or following the machine name denoted by a '#' symbol. -# -# For example: -# -# 102.54.94.97 rhino.acme.com # source server -# 38.25.63.10 x.acme.com # x client host - -# localhost name resolution is handled within DNS itself. -127.0.0.1 localhost -::1 localhost -#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know - -127.0.0.1 mydomain.com -127.0.0.1 admin.mydomain.com - -#-END iris- diff --git a/_examples/routing/subdomains/single/main.go b/_examples/routing/subdomains/single/main.go deleted file mode 100644 index ef972c050d..0000000000 --- a/_examples/routing/subdomains/single/main.go +++ /dev/null @@ -1,44 +0,0 @@ -// Package main register static subdomains, simple as parties, check ./hosts if you use windows -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - // Subdomain method is just another Party. - admin := app.Subdomain("admin") - { - // admin.mydomain.com - admin.Get("/", func(c iris.Context) { - c.Writef("INDEX FROM admin.mydomain.com") - }) - // admin.mydomain.com/hey - admin.Get("/hey", func(c iris.Context) { - c.Writef("HEY FROM admin.mydomain.com/hey") - }) - // admin.mydomain.com/hey2 - admin.Get("/hey2", func(c iris.Context) { - c.Writef("HEY SECOND FROM admin.mydomain.com/hey") - }) - } - - // mydomain.com - app.Get("/", func(c iris.Context) { - c.Writef("INDEX FROM no-subdomain hey") - }) - - // mydomain.com/hey - app.Get("/hey", func(c iris.Context) { - c.Writef("HEY FROM no-subdomain hey") - }) - - // http://admin.mydomain.com - // http://admin.mydomain.com/hey - // http://admin.mydomain.com/hey2 - // http://mydomain.com - // http://mydomain.com/hey - app.Listen("mydomain.com:80") // for beginners: look ../hosts file -} diff --git a/_examples/routing/subdomains/wildcard/hosts b/_examples/routing/subdomains/wildcard/hosts deleted file mode 100644 index 3b3b566475..0000000000 --- a/_examples/routing/subdomains/wildcard/hosts +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) 1993-2009 Microsoft Corp. -# -# This is a sample HOSTS file used by Microsoft TCP/IP for Windows. -# -# This file contains the mappings of IP addresses to host names. Each -# entry should be kept on an individual line. The IP address should -# be placed in the first column followed by the corresponding host name. -# The IP address and the host name should be separated by at least one -# space. -# -# Additionally, comments (such as these) may be inserted on individual -# lines or following the machine name denoted by a '#' symbol. -# -# For example: -# -# 102.54.94.97 rhino.acme.com # source server -# 38.25.63.10 x.acme.com # x client host - -# localhost name resolution is handled within DNS itself. -127.0.0.1 localhost -::1 localhost -#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know -127.0.0.1 mydomain.com -127.0.0.1 username1.mydomain.com -127.0.0.1 username2.mydomain.com -127.0.0.1 username3.mydomain.com -127.0.0.1 username4.mydomain.com -127.0.0.1 username5.mydomain.com - -#-END iris- diff --git a/_examples/routing/subdomains/wildcard/main.go b/_examples/routing/subdomains/wildcard/main.go deleted file mode 100644 index 2b78dfe803..0000000000 --- a/_examples/routing/subdomains/wildcard/main.go +++ /dev/null @@ -1,71 +0,0 @@ -// Package main an example on how to catch dynamic subdomains - wildcard. -// On the first example we learnt how to create routes for static subdomains, subdomains you know that you will have. -// Here we will see an example how to catch unknown subdomains, dynamic subdomains, like username.mydomain.com:8080. -package main - -import ( - "github.com/kataras/iris/v12" -) - -// register a dynamic-wildcard subdomain to your server machine(dns/...) first, check ./hosts if you use windows. -// run this file and try to redirect: http://username1.mydomain.com:8080/ , http://username2.mydomain.com:8080/ , http://username1.mydomain.com/something, http://username1.mydomain.com/something/sadsadsa - -func main() { - app := iris.New() - - /* Keep note that you can use both type of subdomains (named and wildcard(*.) ) - admin.mydomain.com, and for other the Party(*.) but this is not this example's purpose - - admin := app.Party("admin.") - { - // admin.mydomain.com - admin.Get("/", func(ctx iris.Context) { - ctx.Writef("INDEX FROM admin.mydomain.com") - }) - // admin.mydomain.com/hey - admin.Get("/hey", func(ctx iris.Context) { - ctx.Writef("HEY FROM admin.mydomain.com/hey") - }) - // admin.mydomain.com/hey2 - admin.Get("/hey2", func(ctx iris.Context) { - ctx.Writef("HEY SECOND FROM admin.mydomain.com/hey") - }) - }*/ - - // no order, you can register subdomains at the end also. - dynamicSubdomains := app.Party("*.") - { - dynamicSubdomains.Get("/", dynamicSubdomainHandler) - - dynamicSubdomains.Get("/something", dynamicSubdomainHandler) - - dynamicSubdomains.Get("/something/{paramfirst}", dynamicSubdomainHandlerWithParam) - } - - app.Get("/", func(ctx iris.Context) { - ctx.Writef("Hello from mydomain.com path: %s", ctx.Path()) - }) - - app.Get("/hello", func(ctx iris.Context) { - ctx.Writef("Hello from mydomain.com path: %s", ctx.Path()) - }) - - // http://mydomain.com:8080 - // http://username1.mydomain.com:8080 - // http://username2.mydomain.com:8080/something - // http://username3.mydomain.com:8080/something/yourname - app.Listen("mydomain.com:8080") // for beginners: look ../hosts file -} - -func dynamicSubdomainHandler(ctx iris.Context) { - username := ctx.Subdomain() - ctx.Writef("Hello from dynamic subdomain path: %s, here you can handle the route for dynamic subdomains, handle the user: %s", ctx.Path(), username) - // if http://username4.mydomain.com:8080/ prints: - // Hello from dynamic subdomain path: /, here you can handle the route for dynamic subdomains, handle the user: username4 -} - -func dynamicSubdomainHandlerWithParam(ctx iris.Context) { - username := ctx.Subdomain() - ctx.Writef("Hello from dynamic subdomain path: %s, here you can handle the route for dynamic subdomains, handle the user: %s", ctx.Path(), username) - ctx.Writef("The paramfirst is: %s", ctx.Params().Get("paramfirst")) -} diff --git a/_examples/routing/subdomains/www/hosts b/_examples/routing/subdomains/www/hosts deleted file mode 100644 index ab30435658..0000000000 --- a/_examples/routing/subdomains/www/hosts +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) 1993-2009 Microsoft Corp. -# -# This is a sample HOSTS file used by Microsoft TCP/IP for Windows. -# -# This file contains the mappings of IP addresses to host names. Each -# entry should be kept on an individual line. The IP address should -# be placed in the first column followed by the corresponding host name. -# The IP address and the host name should be separated by at least one -# space. -# -# Additionally, comments (such as these) may be inserted on individual -# lines or following the machine name denoted by a '#' symbol. -# -# For example: -# -# 102.54.94.97 rhino.acme.com # source server -# 38.25.63.10 x.acme.com # x client host - -# localhost name resolution is handled within DNS itself. -127.0.0.1 localhost -::1 localhost -#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know -127.0.0.1 mydomain.com -127.0.0.1 www.mydomain.com -#-END iris- diff --git a/_examples/routing/subdomains/www/main.go b/_examples/routing/subdomains/www/main.go deleted file mode 100644 index 280179a65b..0000000000 --- a/_examples/routing/subdomains/www/main.go +++ /dev/null @@ -1,79 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func newApp() *iris.Application { - app := iris.New() - - app.Get("/", info) - app.Get("/about", info) - app.Get("/contact", info) - - app.PartyFunc("/api/users", func(r iris.Party) { - r.Get("/", info) - r.Get("/{id:uint64}", info) - - r.Post("/", info) - - r.Put("/{id:uint64}", info) - }) /* <- same as: - usersAPI := app.Party("/api/users") - { // those brackets are just syntactic-sugar things. - // This method is rarely used but you can make use of it when you want - // scoped variables to that code block only. - usersAPI.Get/Post... - } - usersAPI.Get/Post... - */ - - www := app.Party("www.") - { - // Just to show how you can get all routes and copy them to another - // party or subdomain: - // Get all routes that are registered so far, including all "Parties" and subdomains: - currentRoutes := app.GetRoutes() - // Register them to the www subdomain/vhost as well: - for _, r := range currentRoutes { - www.Handle(r.Method, r.Tmpl().Src, r.Handlers...) - } - - // http://www.mydomain.com/hi - www.Get("/hi", func(ctx iris.Context) { - ctx.Writef("hi from www.mydomain.com") - }) - } - // See also the "subdomains/redirect" to register redirect router wrappers between subdomains, - // i.e mydomain.com to www.mydomain.com (like facebook does for SEO reasons(;)). - - return app -} - -func main() { - app := newApp() - // http://mydomain.com - // http://mydomain.com/about - // http://imydomain.com/contact - // http://mydomain.com/api/users - // http://mydomain.com/api/users/42 - - // http://www.mydomain.com - // http://www.mydomain.com/hi - // http://www.mydomain.com/about - // http://www.mydomain.com/contact - // http://www.mydomain.com/api/users - // http://www.mydomain.com/api/users/42 - if err := app.Listen("mydomain.com:80"); err != nil { - panic(err) - } -} - -func info(ctx iris.Context) { - method := ctx.Method() - subdomain := ctx.Subdomain() - path := ctx.Path() - - ctx.Writef("\nInfo\n\n") - ctx.Writef("Method: %s\nSubdomain: %s\nPath: %s", method, subdomain, path) -} diff --git a/_examples/routing/subdomains/www/main_test.go b/_examples/routing/subdomains/www/main_test.go deleted file mode 100644 index 10686065f5..0000000000 --- a/_examples/routing/subdomains/www/main_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package main - -import ( - "fmt" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -type testRoute struct { - path string - method string - subdomain string -} - -func (r testRoute) response() string { - msg := fmt.Sprintf("\nInfo\n\nMethod: %s\nSubdomain: %s\nPath: %s", r.method, r.subdomain, r.path) - return msg -} - -func TestSubdomainWWW(t *testing.T) { - app := newApp() - - tests := []testRoute{ - // host - {"/", "GET", ""}, - {"/about", "GET", ""}, - {"/contact", "GET", ""}, - {"/api/users", "GET", ""}, - {"/api/users/42", "GET", ""}, - {"/api/users", "POST", ""}, - {"/api/users/42", "PUT", ""}, - // www sub domain - {"/", "GET", "www"}, - {"/about", "GET", "www"}, - {"/contact", "GET", "www"}, - {"/api/users", "GET", "www"}, - {"/api/users/42", "GET", "www"}, - {"/api/users", "POST", "www"}, - {"/api/users/42", "PUT", "www"}, - } - - host := "localhost:1111" - e := httptest.New(t, app, httptest.Debug(false)) - - for _, test := range tests { - - req := e.Request(test.method, test.path) - if subdomain := test.subdomain; subdomain != "" { - req.WithURL("http://" + subdomain + "." + host) - } - - req.Expect(). - Status(httptest.StatusOK). - Body().Equal(test.response()) - } -} diff --git a/_examples/routing/versioning/main.go b/_examples/routing/versioning/main.go deleted file mode 100644 index 3502173fff..0000000000 --- a/_examples/routing/versioning/main.go +++ /dev/null @@ -1,78 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/versioning" -) - -func main() { - app := iris.New() - - examplePerRoute(app) - examplePerParty(app) - - // Read the README.md before any action. - app.Listen(":8080") -} - -// How to test: -// Open Postman -// GET: localhost:8080/api/cats -// Headers[1] = Accept-Version: "1" and repeat with -// Headers[1] = Accept-Version: "2.5" -// or even "Accept": "application/json; version=2.5" -func examplePerRoute(app *iris.Application) { - app.Get("/api/cats", versioning.NewMatcher(versioning.Map{ - "1": catsVersionExactly1Handler, - ">= 2, < 3": catsV2Handler, - versioning.NotFound: versioning.NotFoundHandler, - })) -} - -// How to test: -// Open Postman -// GET: localhost:8080/api/users -// Headers[1] = Accept-Version: "1.9.9" and repeat with -// Headers[1] = Accept-Version: "2.5" -// -// POST: localhost:8080/api/users/new -// Headers[1] = Accept-Version: "1.8.3" -// -// POST: localhost:8080/api/users -// Headers[1] = Accept-Version: "2" -func examplePerParty(app *iris.Application) { - usersAPI := app.Party("/api/users") - // You can customize the way a version is extracting - // via middleware, for example: - // version url parameter, and, if it's missing we default it to "1". - usersAPI.Use(func(ctx iris.Context) { - versioning.SetVersion(ctx, ctx.URLParamDefault("version", "1")) - ctx.Next() - }) - - // version 1. - usersAPIV1 := versioning.NewGroup(usersAPI, ">= 1, < 2") - usersAPIV1.Get("/", func(ctx iris.Context) { - ctx.Writef("v1 resource: /api/users handler") - }) - usersAPIV1.Post("/new", func(ctx iris.Context) { - ctx.Writef("v1 resource: /api/users/new post handler") - }) - - // version 2. - usersAPIV2 := versioning.NewGroup(usersAPI, ">= 2, < 3") - usersAPIV2.Get("/", func(ctx iris.Context) { - ctx.Writef("v2 resource: /api/users handler") - }) - usersAPIV2.Post("/", func(ctx iris.Context) { - ctx.Writef("v2 resource: /api/users post handler") - }) -} - -func catsVersionExactly1Handler(ctx iris.Context) { - ctx.Writef("v1 exactly resource: /api/cats handler") -} - -func catsV2Handler(ctx iris.Context) { - ctx.Writef("v2 resource: /api/cats handler") -} diff --git a/_examples/routing/writing-a-middleware/globally/main.go b/_examples/routing/writing-a-middleware/globally/main.go deleted file mode 100644 index 2cd92e9437..0000000000 --- a/_examples/routing/writing-a-middleware/globally/main.go +++ /dev/null @@ -1,69 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - // register the "before" handler as the first handler which will be executed - // on all domain's routes. - // Or use the `UseGlobal` to register a middleware which will fire across subdomains. - // app.Use(before) - // register the "after" handler as the last handler which will be executed - // after all domain's routes' handler(s). - // - // Or use the `DoneGlobal` to append handlers that will be fired globally. - // app.Done(after) - - // register our routes. - app.Get("/", indexHandler) - app.Get("/contact", contactHandler) - - // Order of those calls doesn't matter, `UseGlobal` and `DoneGlobal` - // are applied to existing routes and future routes. - // - // Remember: the `Use` and `Done` are applied to the current party's and its children, - // so if we used the `app.Use/Done` before the routes registration - // it would work like UseGlobal/DoneGlobal in this case, because the `app` is the root party. - // - // See `app.Party/PartyFunc` for more. - app.UseGlobal(before) - app.DoneGlobal(after) - - app.Listen(":8080") -} - -func before(ctx iris.Context) { - shareInformation := "this is a sharable information between handlers" - - requestPath := ctx.Path() - println("Before the indexHandler or contactHandler: " + requestPath) - - ctx.Values().Set("info", shareInformation) - ctx.Next() -} - -func after(ctx iris.Context) { - println("After the indexHandler or contactHandler") -} - -func indexHandler(ctx iris.Context) { - println("Inside indexHandler") - - // take the info from the "before" handler. - info := ctx.Values().GetString("info") - - // write something to the client as a response. - ctx.HTML("

Response

") - ctx.HTML("
Info: " + info) - - ctx.Next() // execute the "after" handler registered via `DoneGlobal`. -} - -func contactHandler(ctx iris.Context) { - println("Inside contactHandler") - - // write something to the client as a response. - ctx.HTML("

Contact

") - - ctx.Next() // execute the "after" handler registered via `DoneGlobal`. -} diff --git a/_examples/routing/writing-a-middleware/per-route/main.go b/_examples/routing/writing-a-middleware/per-route/main.go deleted file mode 100644 index 223a019f8d..0000000000 --- a/_examples/routing/writing-a-middleware/per-route/main.go +++ /dev/null @@ -1,69 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - // or app.Use(before) and app.Done(after). - app.Get("/", before, mainHandler, after) - - // Use registers a middleware(prepend handlers) to all party's, and its children that will be registered - // after. - // - // (`app` is the root children so those use and done handlers will be registered everywhere) - app.Use(func(ctx iris.Context) { - println(`before the party's routes and its children, -but this is not applied to the '/' route -because it's registered before the middleware, order matters.`) - ctx.Next() - }) - - app.Done(func(ctx iris.Context) { - println("this is executed always last, if the previous handler calls the `ctx.Next()`, it's the reverse of `.Use`") - message := ctx.Values().GetString("message") - println("message: " + message) - }) - - app.Get("/home", func(ctx iris.Context) { - ctx.HTML("

Home

") - ctx.Values().Set("message", "this is the home message, ip: "+ctx.RemoteAddr()) - ctx.Next() // call the done handlers. - }) - - child := app.Party("/child") - child.Get("/", func(ctx iris.Context) { - ctx.Writef(`this is the localhost:8080/child route. -All Use and Done handlers that are registered to the parent party, -are applied here as well.`) - ctx.Next() // call the done handlers. - }) - - app.Listen(":8080") -} - -func before(ctx iris.Context) { - shareInformation := "this is a sharable information between handlers" - - requestPath := ctx.Path() - println("Before the mainHandler: " + requestPath) - - ctx.Values().Set("info", shareInformation) - ctx.Next() // execute the next handler, in this case the main one. -} - -func after(ctx iris.Context) { - println("After the mainHandler") -} - -func mainHandler(ctx iris.Context) { - println("Inside mainHandler") - - // take the info from the "before" handler. - info := ctx.Values().GetString("info") - - // write something to the client as a response. - ctx.HTML("

Response

") - ctx.HTML("
Info: " + info) - - ctx.Next() // execute the "after". -} diff --git a/_examples/sessions/basic/main.go b/_examples/sessions/basic/main.go deleted file mode 100644 index 5827ed886c..0000000000 --- a/_examples/sessions/basic/main.go +++ /dev/null @@ -1,51 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - - "github.com/kataras/iris/v12/sessions" -) - -const cookieNameForSessionID = "session_id_cookie" - -func secret(ctx iris.Context) { - // Check if user is authenticated - if auth, _ := sessions.Get(ctx).GetBoolean("authenticated"); !auth { - ctx.StatusCode(iris.StatusForbidden) - return - } - - // Print secret message - ctx.WriteString("The cake is a lie!") -} - -func login(ctx iris.Context) { - session := sessions.Get(ctx) - - // Authentication goes here - // ... - - // Set user as authenticated - session.Set("authenticated", true) -} - -func logout(ctx iris.Context) { - session := sessions.Get(ctx) - - // Revoke users authentication - session.Set("authenticated", false) -} - -func main() { - app := iris.New() - sess := sessions.New(sessions.Config{Cookie: cookieNameForSessionID, AllowReclaim: true}) - app.Use(sess.Handler()) - // ^ or comment this line and use sess.Start(ctx) inside your handlers - // instead of sessions.Get(ctx). - - app.Get("/secret", secret) - app.Get("/login", login) - app.Get("/logout", logout) - - app.Listen(":8080") -} diff --git a/_examples/sessions/database/badger/main.go b/_examples/sessions/database/badger/main.go deleted file mode 100644 index d49c1dbb56..0000000000 --- a/_examples/sessions/database/badger/main.go +++ /dev/null @@ -1,56 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12" - - "github.com/kataras/iris/v12/sessions" - "github.com/kataras/iris/v12/sessions/sessiondb/badger" - - "github.com/kataras/iris/v12/_examples/sessions/overview/example" -) - -func main() { - db, err := badger.New("./data") - if err != nil { - panic(err) - } - - // close and unlock the database when control+C/cmd+C pressed - iris.RegisterOnInterrupt(func() { - db.Close() - }) - - defer db.Close() // close and unlock the database if application errored. - - // The default transcoder is the JSON one, - // based on the https://golang.org/pkg/encoding/json/#Unmarshal - // you can only retrieve numbers as float64 types: - // * bool, for booleans - // * float64, for numbers - // * string, for strings - // * []interface{}, for arrays - // * map[string]interface{}, for objects. - // If you want to save the data per go-specific types - // you should change the DefaultTranscoder to the GobTranscoder one: - // sessions.DefaultTranscoder = sessions.GobTranscoder{} - - sess := sessions.New(sessions.Config{ - Cookie: "sessionscookieid", - Expires: 1 * time.Minute, // <=0 means unlimited life. Defaults to 0. - AllowReclaim: true, - }) - - sess.OnDestroy(func(sid string) { - println(sid + " expired and destroyed from memory and its values from database") - }) - - // - // IMPORTANT: - // - sess.UseDatabase(db) - - app := example.NewApp(sess) - app.Listen(":8080") -} diff --git a/_examples/sessions/database/boltdb/main.go b/_examples/sessions/database/boltdb/main.go deleted file mode 100644 index 11bc96210b..0000000000 --- a/_examples/sessions/database/boltdb/main.go +++ /dev/null @@ -1,63 +0,0 @@ -package main - -import ( - "os" - "time" - - "github.com/kataras/iris/v12" - - "github.com/kataras/iris/v12/sessions" - "github.com/kataras/iris/v12/sessions/sessiondb/boltdb" - - "github.com/kataras/iris/v12/_examples/sessions/overview/example" -) - -func main() { - db, err := boltdb.New("./sessions.db", os.FileMode(0750)) - if err != nil { - panic(err) - } - - // close and unlobkc the database when control+C/cmd+C pressed - iris.RegisterOnInterrupt(func() { - db.Close() - }) - - defer db.Close() // close and unlock the database if application errored. - sess := sessions.New(sessions.Config{ - Cookie: "sessionscookieid", - Expires: 45 * time.Minute, // <=0 means unlimited life. Defaults to 0. - AllowReclaim: true, - }) - - // - // IMPORTANT: - // - sess.UseDatabase(db) - - // The default database's values encoder and decoder - // calls the value's `Marshal/Unmarshal` methods (if any) - // otherwise JSON is selected, - // the JSON format can be stored to any database and - // it supports both builtin language types(e.g. string, int) and custom struct values. - // Also, and the most important, the values can be - // retrieved/logged/monitored by a third-party program - // written in any other language as well. - // - // You can change this behavior by registering a custom `Transcoder`. - // Iris provides a `GobTranscoder` which is mostly suitable - // if your session values are going to be custom Go structs. - // Select this if you always retrieving values through Go. - // Don't forget to initialize a call of gob.Register when necessary. - // Read https://golang.org/pkg/encoding/gob/ for more. - // - // You can also implement your own `sessions.Transcoder` and use it, - // i.e: a transcoder which will allow(on Marshal: return its byte representation and nil error) - // or dissalow(on Marshal: return non nil error) certain types. - // - // gob.Register(example.BusinessModel{}) - // sessions.DefaultTranscoder = sessions.GobTranscoder{} - - app := example.NewApp(sess) - app.Listen(":8080") -} diff --git a/_examples/sessions/database/redis/Dockerfile b/_examples/sessions/database/redis/Dockerfile deleted file mode 100644 index a1d50355d2..0000000000 --- a/_examples/sessions/database/redis/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM golang:latest AS builder -RUN apt-get update -WORKDIR /go/src/app -ENV GO111MODULE=on \ - CGO_ENABLED=0 \ - GOOS=linux \ - GOARCH=amd64 -# Caching go modules and build the binary. -COPY go.mod . -RUN go mod download -COPY . . -RUN go install - -FROM scratch -COPY --from=builder /go/bin/app . -ENTRYPOINT ["./app"] \ No newline at end of file diff --git a/_examples/sessions/database/redis/docker-compose.yml b/_examples/sessions/database/redis/docker-compose.yml deleted file mode 100644 index 324b7534e1..0000000000 --- a/_examples/sessions/database/redis/docker-compose.yml +++ /dev/null @@ -1,24 +0,0 @@ -# docker-compose up [--build] -version: '3' - -services: - redis-server: - image: redis - app1: - build: . - depends_on: - - redis-server - ports: - - 8080:8080 - environment: - - PORT=8080 - - REDIS_ADDR=redis-server:6379 - app2: - build: . - depends_on: - - redis-server - ports: - - 9090:9090 - environment: - - PORT=9090 - - REDIS_ADDR=redis-server:6379 \ No newline at end of file diff --git a/_examples/sessions/database/redis/go.mod b/_examples/sessions/database/redis/go.mod deleted file mode 100644 index 549a58286c..0000000000 --- a/_examples/sessions/database/redis/go.mod +++ /dev/null @@ -1,6 +0,0 @@ -module app - -go 1.15 - -require github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd - diff --git a/_examples/sessions/database/redis/main.go b/_examples/sessions/database/redis/main.go deleted file mode 100644 index f8333dc7d4..0000000000 --- a/_examples/sessions/database/redis/main.go +++ /dev/null @@ -1,77 +0,0 @@ -package main - -import ( - "fmt" - "os" - "strings" - "time" - - "github.com/kataras/iris/v12" - - "github.com/kataras/iris/v12/sessions" - "github.com/kataras/iris/v12/sessions/sessiondb/redis" - - "github.com/kataras/iris/v12/_examples/sessions/overview/example" -) - -// tested with redis version 3.0.503. -// for windows see: https://github.com/ServiceStack/redis-windows -func main() { - // These are the default values, - // you can replace them based on your running redis' server settings: - db := redis.New(redis.Config{ - Network: "tcp", - Addr: getenv("REDIS_ADDR", "127.0.0.1:6379"), - Timeout: time.Duration(30) * time.Second, - MaxActive: 10, - Password: "", - Database: "", - Prefix: "", - Delim: "-", - Driver: redis.Redigo(), // redis.Radix() can be used instead. - }) - - // Optionally configure the underline driver: - // driver := redis.Redigo() - // driver.MaxIdle = ... - // driver.IdleTimeout = ... - // driver.Wait = ... - // redis.Config {Driver: driver} - - // Close connection when control+C/cmd+C - iris.RegisterOnInterrupt(func() { - db.Close() - }) - - defer db.Close() // close the database connection if application errored. - - sess := sessions.New(sessions.Config{ - Cookie: "_session_id", - Expires: 0, // defaults to 0: unlimited life. Another good value is: 45 * time.Minute, - AllowReclaim: true, - CookieSecureTLS: true, - }) - - // - // IMPORTANT: - // - sess.UseDatabase(db) - - app := example.NewApp(sess) - - // TIP scaling-out Iris sessions using redis: - // $ docker-compose up - // http://localhost:8080/set/$key/$value - // The value will be available on all Iris servers as well. - // E.g. http://localhost:9090/get/$key and vice versa. - addr := fmt.Sprintf(":%s", getenv("PORT", "8080")) - app.Listen(addr) -} - -func getenv(key string, def string) string { - if v := os.Getenv(strings.ToUpper(key)); v != "" { - return v - } - - return def -} diff --git a/_examples/sessions/flash-messages/main.go b/_examples/sessions/flash-messages/main.go deleted file mode 100644 index acd2a5ddca..0000000000 --- a/_examples/sessions/flash-messages/main.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - - "github.com/kataras/iris/v12/sessions" -) - -func main() { - app := iris.New() - - sess := sessions.New(sessions.Config{Cookie: "_session_id", AllowReclaim: true}) - app.Use(sess.Handler()) - - app.Get("/set", func(ctx iris.Context) { - s := sessions.Get(ctx) - s.SetFlash("name", "iris") - ctx.Writef("Message set, is available for the next request") - }) - - app.Get("/get", func(ctx iris.Context) { - s := sessions.Get(ctx) - name := s.GetFlashString("name") - if name == "" { - ctx.Writef("Empty name!!") - return - } - ctx.Writef("Hello %s", name) - }) - - app.Get("/test", func(ctx iris.Context) { - s := sessions.Get(ctx) - name := s.GetFlashString("name") - if name == "" { - ctx.Writef("Empty name!!") - return - } - - ctx.Writef("Ok you are coming from /set ,the value of the name is %s", name) - ctx.Writef(", and again from the same context: %s", name) - }) - - app.Listen(":8080") -} diff --git a/_examples/sessions/overview/example/example.go b/_examples/sessions/overview/example/example.go deleted file mode 100644 index 64509b5133..0000000000 --- a/_examples/sessions/overview/example/example.go +++ /dev/null @@ -1,187 +0,0 @@ -package example - -import ( - "errors" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/sessions" -) - -// BusinessModel is just a Go struct value that we will use in our session example, -// never save sensitive information, like passwords, here. -type BusinessModel struct { - Name string -} - -// NewApp returns a new application for showcasing the sessions feature. -func NewApp(sess *sessions.Sessions) *iris.Application { - app := iris.New() - app.Use(sess.Handler()) // session is always non-nil inside handlers now. - - app.Get("/", func(ctx iris.Context) { - session := sessions.Get(ctx) // same as sess.Start(ctx, cookieOptions...) - if session.Len() == 0 { - ctx.HTML(`no session values stored yet. Navigate to: set page`) - return - } - - ctx.HTML("
    ") - session.Visit(func(key string, value interface{}) { - ctx.HTML("
  • %s = %v
  • ", key, value) - }) - - ctx.HTML("
") - }) - - // set session values. - app.Get("/set", func(ctx iris.Context) { - session := sessions.Get(ctx) - isNew := session.IsNew() - - session.Set("name", "iris") - - ctx.Writef("All ok session set to: %s [isNew=%t]", session.GetString("name"), isNew) - }) - - app.Get("/get", func(ctx iris.Context) { - session := sessions.Get(ctx) - - // get a specific value, as string, - // if not found then it returns just an empty string. - name := session.GetString("name") - - ctx.Writef("The name on the /set was: %s", name) - }) - - app.Get("/set-struct", func(ctx iris.Context) { - session := sessions.Get(ctx) - session.Set("struct", BusinessModel{Name: "John Doe"}) - - ctx.Writef("All ok session value of the 'struct' is: %v", session.Get("struct")) - }) - - app.Get("/get-struct", func(ctx iris.Context) { - session := sessions.Get(ctx) - ctx.Writef("Session value of the 'struct' is: %v", session.Get("struct")) - }) - - app.Get("/set/{key}/{value}", func(ctx iris.Context) { - session := sessions.Get(ctx) - - key := ctx.Params().Get("key") - value := ctx.Params().Get("value") - isNew := session.IsNew() - - session.Set(key, value) - - ctx.Writef("All ok session value of the '%s' is: %s [isNew=%t]", key, session.GetString(key), isNew) - }) - - app.Get("/get/{key}", func(ctx iris.Context) { - session := sessions.Get(ctx) - // get a specific key, as string, if no found returns just an empty string - key := ctx.Params().Get("key") - value := session.Get(key) - - ctx.Writef("The [%s:%T] on the /set was: %v", key, value, value) - }) - - app.Get("/set/{type}/{key}/{value}", func(ctx iris.Context) { - session := sessions.Get(ctx) - - key := ctx.Params().Get("key") - var value interface{} - - switch ctx.Params().Get("type") { - case "int": - value = ctx.Params().GetIntDefault("value", 0) - case "float64": - value = ctx.Params().GetFloat64Default("value", 0.0) - default: - value = ctx.Params().Get("value") - } - session.Set(key, value) - - value = session.Get(key) - ctx.Writef("Key: %s, Type: %T, Value: %v", key, value, value) - }) - - app.Get("/delete", func(ctx iris.Context) { - session := sessions.Get(ctx) - // delete a specific key - session.Delete("name") - }) - - app.Get("/clear", func(ctx iris.Context) { - session := sessions.Get(ctx) - // removes all entries. - session.Clear() - }) - - app.Get("/update", func(ctx iris.Context) { - session := sessions.Get(ctx) - // shifts the expiration based on the session's `Lifetime`. - if err := session.Man.ShiftExpiration(ctx); err != nil { - if errors.Is(err, sessions.ErrNotFound) { - ctx.StatusCode(iris.StatusNotFound) - } else if errors.Is(err, sessions.ErrNotImplemented) { - ctx.StatusCode(iris.StatusNotImplemented) - } else { - ctx.StatusCode(iris.StatusNotModified) - } - - ctx.Writef("%v", err) - ctx.Application().Logger().Error(err) - } - }) - - app.Get("/destroy", func(ctx iris.Context) { - session := sessions.Get(ctx) - // Man(anager)'s Destroy, removes the entire session data and cookie - session.Man.Destroy(ctx) - }) - - // Note about Destroy: - // - // You can destroy a session outside of a handler too, using the: - // sess.DestroyByID - // sess.DestroyAll - - // remember: slices and maps are muttable by-design - // The `SetImmutable` makes sure that they will be stored and received - // as immutable, so you can't change them directly by mistake. - // - // Use `SetImmutable` consistently, it's slower than `Set`. - // Read more about muttable and immutable go types: https://stackoverflow.com/a/8021081 - app.Get("/set-immutable", func(ctx iris.Context) { - session := sessions.Get(ctx) - - business := []BusinessModel{{Name: "Edward"}, {Name: "value 2"}} - session.SetImmutable("businessEdit", business) - businessGet := session.Get("businessEdit").([]BusinessModel) - - // try to change it, if we used `Set` instead of `SetImmutable` this - // change will affect the underline array of the session's value "businessEdit", but now it will not. - businessGet[0].Name = "Gabriel" - }) - - app.Get("/get-immutable", func(ctx iris.Context) { - valSlice := sessions.Get(ctx).Get("businessEdit") - if valSlice == nil { - ctx.HTML("please navigate to the /set-immutable first") - return - } - - firstModel := valSlice.([]BusinessModel)[0] - // businessGet[0].Name is equal to Edward initially - if firstModel.Name != "Edward" { - panic("Report this as a bug, immutable data cannot be changed from the caller without re-SetImmutable") - } - - ctx.Writef("[]businessModel[0].Name remains: %s", firstModel.Name) - - // the name should remains "Edward" - }) - - return app -} diff --git a/_examples/sessions/overview/main.go b/_examples/sessions/overview/main.go deleted file mode 100644 index d04462ae00..0000000000 --- a/_examples/sessions/overview/main.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12/_examples/sessions/overview/example" - "github.com/kataras/iris/v12/sessions" -) - -func main() { - sess := sessions.New(sessions.Config{ - // Cookie string, the session's client cookie name, for example: "_session_id" - // - // Defaults to "irissessionid" - Cookie: "_session_id", - // it's time.Duration, from the time cookie is created, how long it can be alive? - // 0 means no expire, unlimited life. - // -1 means expire when browser closes - // or set a value, like 2 hours: - Expires: time.Hour * 2, - // if you want to invalid cookies on different subdomains - // of the same host, then enable it. - // Defaults to false. - DisableSubdomainPersistence: false, - // Allow getting the session value stored by the request from the same request. - AllowReclaim: true, - /* - SessionIDGenerator: func(ctx iris.Context) string { - id:= ctx.GetHeader("X-Session-Id") - if id == "" { - id = // [generate ID here and set the header] - ctx.Header("X-Session-Id", id) - } - - return id - }, - */ - }) - - app := example.NewApp(sess) - app.Listen(":8080") -} diff --git a/_examples/sessions/securecookie/main.go b/_examples/sessions/securecookie/main.go deleted file mode 100644 index b268662697..0000000000 --- a/_examples/sessions/securecookie/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -// developers can use any library to add a custom cookie encoder/decoder. -// At this example we use the gorilla's securecookie package: -// $ go get github.com/gorilla/securecookie -// $ go run main.go - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/sessions" - - "github.com/kataras/iris/v12/_examples/sessions/overview/example" - - "github.com/gorilla/securecookie" -) - -func newApp() *iris.Application { - app := iris.New() - - cookieName := "_session_id" - // AES only supports key sizes of 16, 24 or 32 bytes. - // You either need to provide exactly that amount or you derive the key from what you type in. - hashKey := securecookie.GenerateRandomKey(64) - blockKey := securecookie.GenerateRandomKey(32) - s := securecookie.New(hashKey, blockKey) - - mySessions := sessions.New(sessions.Config{ - Cookie: cookieName, - Encoding: s, - AllowReclaim: true, - }) - - app = example.NewApp(mySessions) - return app -} - -func main() { - app := newApp() - app.Listen(":8080") -} diff --git a/_examples/sessions/securecookie/main_test.go b/_examples/sessions/securecookie/main_test.go deleted file mode 100644 index 775c698dd8..0000000000 --- a/_examples/sessions/securecookie/main_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/httptest" -) - -func TestSessionsEncodeDecode(t *testing.T) { - app := newApp() - e := httptest.New(t, app, httptest.URL("http://example.com")) - - es := e.GET("/set").Expect() - es.Status(iris.StatusOK) - es.Cookies().NotEmpty() - es.Body().Equal("All ok session set to: iris [isNew=true]") - - e.GET("/get").Expect().Status(iris.StatusOK).Body().Equal("The name on the /set was: iris") - // delete and re-get - e.GET("/delete").Expect().Status(iris.StatusOK) - e.GET("/get").Expect().Status(iris.StatusOK).Body().Equal("The name on the /set was: ") - // set, clear and re-get - e.GET("/set").Expect().Body().Equal("All ok session set to: iris [isNew=false]") - e.GET("/clear").Expect().Status(iris.StatusOK) - e.GET("/get").Expect().Status(iris.StatusOK).Body().Equal("The name on the /set was: ") -} diff --git a/_examples/testing/httptest/main.go b/_examples/testing/httptest/main.go deleted file mode 100644 index c86fc7fc77..0000000000 --- a/_examples/testing/httptest/main.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/middleware/basicauth" -) - -func newApp() *iris.Application { - app := iris.New() - - authConfig := basicauth.Config{ - Users: map[string]string{"myusername": "mypassword"}, - } - - authentication := basicauth.New(authConfig) - - app.Get("/", func(ctx iris.Context) { ctx.Redirect("/admin") }) - - // to party - - needAuth := app.Party("/admin", authentication) - { - //http://localhost:8080/admin - needAuth.Get("/", h) - // http://localhost:8080/admin/profile - needAuth.Get("/profile", h) - - // http://localhost:8080/admin/settings - needAuth.Get("/settings", h) - } - - return app -} - -func h(ctx iris.Context) { - username, password, _ := ctx.Request().BasicAuth() - // third parameter it will be always true because the middleware - // makes sure for that, otherwise this handler will not be executed. - - ctx.Writef("%s %s:%s", ctx.Path(), username, password) -} - -func main() { - app := newApp() - app.Listen(":8080") -} diff --git a/_examples/testing/httptest/main_test.go b/_examples/testing/httptest/main_test.go deleted file mode 100644 index 9c3723bcab..0000000000 --- a/_examples/testing/httptest/main_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/httptest" -) - -// $ go test -v -func TestNewApp(t *testing.T) { - app := newApp() - e := httptest.New(t, app) - - // redirects to /admin without basic auth - e.GET("/").Expect().Status(httptest.StatusUnauthorized) - // without basic auth - e.GET("/admin").Expect().Status(httptest.StatusUnauthorized) - - // with valid basic auth - e.GET("/admin").WithBasicAuth("myusername", "mypassword").Expect(). - Status(httptest.StatusOK).Body().Equal("/admin myusername:mypassword") - e.GET("/admin/profile").WithBasicAuth("myusername", "mypassword").Expect(). - Status(httptest.StatusOK).Body().Equal("/admin/profile myusername:mypassword") - e.GET("/admin/settings").WithBasicAuth("myusername", "mypassword").Expect(). - Status(httptest.StatusOK).Body().Equal("/admin/settings myusername:mypassword") - - // with invalid basic auth - e.GET("/admin/settings").WithBasicAuth("invalidusername", "invalidpassword"). - Expect().Status(httptest.StatusUnauthorized) -} - -func TestHandlerUsingNetHTTP(t *testing.T) { - handler := func(ctx iris.Context) { - ctx.WriteString("Hello, World!") - } - - // A shortcut for net/http/httptest.NewRecorder/NewRequest. - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "/", nil) - - httptest.Do(w, r, handler) - if expected, got := "Hello, World!", w.Body.String(); expected != got { - t.Fatalf("expected body: %s but got: %s", expected, got) - } -} diff --git a/_examples/url-shortener/README.md b/_examples/url-shortener/README.md deleted file mode 100644 index 842caed1b2..0000000000 --- a/_examples/url-shortener/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## A URL Shortener Service using Go, Iris and Bolt - -Hackernoon Article: https://medium.com/hackernoon/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7 \ No newline at end of file diff --git a/_examples/url-shortener/factory.go b/_examples/url-shortener/factory.go deleted file mode 100644 index d3e719529c..0000000000 --- a/_examples/url-shortener/factory.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "net/url" - - "github.com/google/uuid" -) - -// Generator the type to generate keys(short urls) -type Generator func() string - -// DefaultGenerator is the defautl url generator -var DefaultGenerator = func() string { - id, _ := uuid.NewRandom() - return id.String() -} - -// Factory is responsible to generate keys(short urls) -type Factory struct { - store Store - generator Generator -} - -// NewFactory receives a generator and a store and returns a new url Factory. -func NewFactory(generator Generator, store Store) *Factory { - return &Factory{ - store: store, - generator: generator, - } -} - -// Gen generates the key. -func (f *Factory) Gen(uri string) (key string, err error) { - // we don't return the parsed url because #hash are converted to uri-compatible - // and we don't want to encode/decode all the time, there is no need for that, - // we save the url as the user expects if the uri validation passed. - _, err = url.ParseRequestURI(uri) - if err != nil { - return "", err - } - - key = f.generator() - // Make sure that the key is unique - for { - if v := f.store.Get(key); v == "" { - break - } - key = f.generator() - } - - return key, nil -} diff --git a/_examples/url-shortener/main.go b/_examples/url-shortener/main.go deleted file mode 100644 index 5e140d6f7c..0000000000 --- a/_examples/url-shortener/main.go +++ /dev/null @@ -1,114 +0,0 @@ -// Package main shows how you can create a simple URL Shortener. -// -// Article: https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7 -// -// $ go get go.etcd.io/bbolt/... -// $ go get github.com/google/uuid -// $ cd $GOPATH/src/github.com/kataras/iris/_examples/url-shortener -// $ go build -// $ ./url-shortener -package main - -import ( - "html/template" - - "github.com/kataras/iris/v12" -) - -func main() { - // assign a variable to the DB so we can use its features later. - db := NewDB("shortener.db") - // Pass that db to our app, in order to be able to test the whole app with a different database later on. - app := newApp(db) - - // release the "db" connection when server goes off. - iris.RegisterOnInterrupt(db.Close) - - app.Listen(":8080") -} - -func newApp(db *DB) *iris.Application { - app := iris.Default() // or app := iris.New() - - // create our factory, which is the manager for the object creation. - // between our web app and the db. - factory := NewFactory(DefaultGenerator, db) - - // serve the "./templates" directory's "*.html" files with the HTML std view engine. - tmpl := iris.HTML("./templates", ".html").Reload(true) - // register any template func(s) here. - // - // Look ./templates/index.html#L16 - tmpl.AddFunc("IsPositive", func(n int) bool { - if n > 0 { - return true - } - return false - }) - - app.RegisterView(tmpl) - - // Serve static files (css) - app.HandleDir("/static", iris.Dir("./resources")) - - indexHandler := func(ctx iris.Context) { - ctx.ViewData("URL_COUNT", db.Len()) - ctx.View("index.html") - } - app.Get("/", indexHandler) - - // find and execute a short url by its key - // used on http://localhost:8080/u/dsaoj41u321dsa - execShortURL := func(ctx iris.Context, key string) { - if key == "" { - ctx.StatusCode(iris.StatusBadRequest) - return - } - - value := db.Get(key) - if value == "" { - ctx.StatusCode(iris.StatusNotFound) - ctx.Writef("Short URL for key: '%s' not found", key) - return - } - - ctx.Redirect(value, iris.StatusTemporaryRedirect) - } - app.Get("/u/{shortkey}", func(ctx iris.Context) { - execShortURL(ctx, ctx.Params().Get("shortkey")) - }) - - app.Post("/shorten", func(ctx iris.Context) { - formValue := ctx.FormValue("url") - if formValue == "" { - ctx.ViewData("FORM_RESULT", "You need to a enter a URL") - ctx.StatusCode(iris.StatusLengthRequired) - } else { - key, err := factory.Gen(formValue) - if err != nil { - ctx.ViewData("FORM_RESULT", "Invalid URL") - ctx.StatusCode(iris.StatusBadRequest) - } else { - if err = db.Set(key, formValue); err != nil { - ctx.ViewData("FORM_RESULT", "Internal error while saving the URL") - app.Logger().Infof("while saving URL: " + err.Error()) - ctx.StatusCode(iris.StatusInternalServerError) - } else { - ctx.StatusCode(iris.StatusOK) - shortenURL := "http://" + app.ConfigurationReadOnly().GetVHost() + "/u/" + key - ctx.ViewData("FORM_RESULT", - template.HTML("
"+shortenURL+" 
")) - } - } - } - - indexHandler(ctx) // no redirect, we need the FORM_RESULT. - }) - - app.Post("/clear_cache", func(ctx iris.Context) { - db.Clear() - ctx.Redirect("/") - }) - - return app -} diff --git a/_examples/url-shortener/main_test.go b/_examples/url-shortener/main_test.go deleted file mode 100644 index 75270e0f5c..0000000000 --- a/_examples/url-shortener/main_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package main - -import ( - "io/ioutil" - "os" - "testing" - "time" - - "github.com/kataras/iris/v12/httptest" -) - -// TestURLShortener tests the simple tasks of our url shortener application. -// Note that it's a pure test. -// The rest possible checks is up to you, take it as as an exercise! -func TestURLShortener(t *testing.T) { - // temp db file - f, err := ioutil.TempFile("", "shortener") - if err != nil { - t.Fatalf("creating temp file for database failed: %v", err) - } - - db := NewDB(f.Name()) - app := newApp(db) - - e := httptest.New(t, app) - originalURL := "https://google.com" - - // save - e.POST("/shorten"). - WithFormField("url", originalURL).Expect(). - Status(httptest.StatusOK).Body().Contains("

-
-
-    
-    Golang URL Shortener
-    
-
-
-
-    

Golang URL Shortener

-

{{ .FORM_RESULT}}

-
- - -
- {{ if IsPositive .URL_COUNT }} -

{{ .URL_COUNT }} URLs shortened

- {{ end }} - -
- -
- - - \ No newline at end of file diff --git a/_examples/view/context-view-data/main.go b/_examples/view/context-view-data/main.go deleted file mode 100644 index 01e3ed88fa..0000000000 --- a/_examples/view/context-view-data/main.go +++ /dev/null @@ -1,55 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12" -) - -const ( - DefaultTitle = "My Awesome Site" - DefaultLayout = "layouts/layout.html" -) - -func main() { - app := iris.New() - // output startup banner and error logs on os.Stdout - - // set the view engine target to ./templates folder - app.RegisterView(iris.HTML("./templates", ".html").Reload(true)) - - app.Use(func(ctx iris.Context) { - // set the title, current time and a layout in order to be used if and when the next handler(s) calls the .Render function - ctx.ViewData("Title", DefaultTitle) - now := time.Now().Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat()) - ctx.ViewData("CurrentTime", now) - ctx.ViewLayout(DefaultLayout) - - ctx.Next() - }) - - app.Get("/", func(ctx iris.Context) { - ctx.ViewData("BodyMessage", "a sample text here... set by the route handler") - if err := ctx.View("index.html"); err != nil { - ctx.Application().Logger().Infof(err.Error()) - } - }) - - app.Get("/about", func(ctx iris.Context) { - ctx.ViewData("Title", "My About Page") - ctx.ViewData("BodyMessage", "about text here... set by the route handler") - - // same file, just to keep things simple. - if err := ctx.View("index.html"); err != nil { - ctx.Application().Logger().Infof(err.Error()) - } - }) - - // http://localhost:8080 - // http://localhost:8080/about - app.Listen(":8080") -} - -// Notes: ViewData("", myCustomStruct{}) will set this myCustomStruct value as a root binding data, -// so any View("other", "otherValue") will probably fail. -// To clear the binding data: ctx.Set(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey(), nil) diff --git a/_examples/view/context-view-data/templates/index.html b/_examples/view/context-view-data/templates/index.html deleted file mode 100644 index a102b2621e..0000000000 --- a/_examples/view/context-view-data/templates/index.html +++ /dev/null @@ -1,8 +0,0 @@ -

- Title: {{.Title}} -

-

{{.BodyMessage}}

- -
- -Current time: {{.CurrentTime}} \ No newline at end of file diff --git a/_examples/view/context-view-data/templates/layouts/layout.html b/_examples/view/context-view-data/templates/layouts/layout.html deleted file mode 100644 index a483377482..0000000000 --- a/_examples/view/context-view-data/templates/layouts/layout.html +++ /dev/null @@ -1,10 +0,0 @@ - - -My WebsiteLayout - - - - - {{ yield }} - - diff --git a/_examples/view/context-view-engine/main.go b/_examples/view/context-view-engine/main.go deleted file mode 100644 index ddc6d90f1e..0000000000 --- a/_examples/view/context-view-engine/main.go +++ /dev/null @@ -1,65 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - // Register a root view engine, as usual, - // will be used to render files through Context.View method - // when no Party or Handler-specific view engine is available. - app.RegisterView(iris.Blocks("./views/public", ".html")) - - // http://localhost:8080 - app.Get("/", index) - - // Register a view engine per group of routes. - adminGroup := app.Party("/admin") - adminGroup.RegisterView(iris.Blocks("./views/admin", ".html")) - - // http://localhost:8080/admin - adminGroup.Get("/", admin) - - // Register a view engine on-fly for the current chain of handlers. - views := iris.Blocks("./views/on-fly", ".html") - if err := views.Load(); err != nil { - app.Logger().Fatal(err) - } - - // http://localhost:8080/on-fly - app.Get("/on-fly", setViews(views), onFly) - - app.Listen(":8080") -} - -func index(ctx iris.Context) { - data := iris.Map{ - "Title": "Public Index Title", - } - - ctx.ViewLayout("main") - ctx.View("index", data) -} - -func admin(ctx iris.Context) { - data := iris.Map{ - "Title": "Admin Panel", - } - - ctx.ViewLayout("main") - ctx.View("index", data) -} - -func setViews(views iris.ViewEngine) iris.Handler { - return func(ctx iris.Context) { - ctx.ViewEngine(views) - ctx.Next() - } -} - -func onFly(ctx iris.Context) { - data := iris.Map{ - "Message": "View engine changed through 'setViews' custom middleware.", - } - - ctx.View("index", data) -} diff --git a/_examples/view/context-view-engine/views/admin/index.html b/_examples/view/context-view-engine/views/admin/index.html deleted file mode 100644 index f537f535d7..0000000000 --- a/_examples/view/context-view-engine/views/admin/index.html +++ /dev/null @@ -1,3 +0,0 @@ -{{ define "content" }} -

Hello, Admin!

-{{ end }} \ No newline at end of file diff --git a/_examples/view/context-view-engine/views/admin/layouts/main.html b/_examples/view/context-view-engine/views/admin/layouts/main.html deleted file mode 100644 index d83d5062c7..0000000000 --- a/_examples/view/context-view-engine/views/admin/layouts/main.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - {{ .Title }} - - - {{ template "content" .}} - - -

Copyright © 2020 Admin

- - \ No newline at end of file diff --git a/_examples/view/context-view-engine/views/on-fly/index.html b/_examples/view/context-view-engine/views/on-fly/index.html deleted file mode 100644 index 4b3fd536f0..0000000000 --- a/_examples/view/context-view-engine/views/on-fly/index.html +++ /dev/null @@ -1,2 +0,0 @@ -

On-fly

-

{{.Message}}

\ No newline at end of file diff --git a/_examples/view/context-view-engine/views/public/500.html b/_examples/view/context-view-engine/views/public/500.html deleted file mode 100644 index 37c58b5884..0000000000 --- a/_examples/view/context-view-engine/views/public/500.html +++ /dev/null @@ -1,12 +0,0 @@ - -{{ define "content" }} -

Internal Server Error

-{{ end }} - -{{ define "message" }} -

{{.Message}}

-{{ end }} \ No newline at end of file diff --git a/_examples/view/context-view-engine/views/public/index.html b/_examples/view/context-view-engine/views/public/index.html deleted file mode 100644 index b11758cf1c..0000000000 --- a/_examples/view/context-view-engine/views/public/index.html +++ /dev/null @@ -1 +0,0 @@ -

Index Body

\ No newline at end of file diff --git a/_examples/view/context-view-engine/views/public/layouts/error.html b/_examples/view/context-view-engine/views/public/layouts/error.html deleted file mode 100644 index 48598789db..0000000000 --- a/_examples/view/context-view-engine/views/public/layouts/error.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - {{.Code}} - - - {{ template "content" .}} - - {{block "message" .}}{{end}} - - \ No newline at end of file diff --git a/_examples/view/context-view-engine/views/public/layouts/main.html b/_examples/view/context-view-engine/views/public/layouts/main.html deleted file mode 100644 index 5b5a509f4c..0000000000 --- a/_examples/view/context-view-engine/views/public/layouts/main.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - {{ if .Title }}{{ .Title }}{{ else }}Default Main Title{{ end }} - - - {{ template "content" . }} - -
{{ partial "partials/footer" .}}
- - \ No newline at end of file diff --git a/_examples/view/context-view-engine/views/public/partials/footer.html b/_examples/view/context-view-engine/views/public/partials/footer.html deleted file mode 100644 index 61ee8461de..0000000000 --- a/_examples/view/context-view-engine/views/public/partials/footer.html +++ /dev/null @@ -1 +0,0 @@ -

Footer Partial

\ No newline at end of file diff --git a/_examples/view/embedding-templates-into-app/bindata.go b/_examples/view/embedding-templates-into-app/bindata.go deleted file mode 100644 index e2a2821be4..0000000000 --- a/_examples/view/embedding-templates-into-app/bindata.go +++ /dev/null @@ -1,319 +0,0 @@ -// Code generated for package main by go-bindata DO NOT EDIT. (@generated) -// sources: -// templates/layouts/layout.html -// templates/layouts/mylayout.html -// templates/page1.html -// templates/partials/page1_partial1.html -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data []byte, name string) ([]byte, error) { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -// Name return file name -func (fi bindataFileInfo) Name() string { - return fi.name -} - -// Size return file size -func (fi bindataFileInfo) Size() int64 { - return fi.size -} - -// Mode return file mode -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} - -// Mode return file modify time -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} - -// IsDir return file whether a directory -func (fi bindataFileInfo) IsDir() bool { - return fi.mode&os.ModeDir != 0 -} - -// Sys return file is sys mode -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -var _templatesLayoutsLayoutHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\xce\xc1\xa9\xc3\x30\x0c\x06\xe0\xf3\x33\x78\x07\xbd\x01\x8c\xc9\x5d\x78\x82\x9e\x4a\x17\x70\x6a\x51\x19\x94\xa4\x38\xca\xc1\x84\xec\x5e\xec\xba\x27\x49\xf0\x89\xff\x47\xd6\x45\x82\x35\xc8\x14\x53\x9b\x9a\x55\x28\xdc\x62\xdd\x0e\x45\xff\xbd\xac\xb1\x06\xfd\x4f\xcc\x5b\xaa\xc1\x9a\x3f\xe4\x29\x3c\x38\xef\x90\x77\x50\x26\x78\xc9\x36\x47\x01\x19\xaf\x3c\x75\x34\x17\xf0\x7d\xf9\x77\x0e\xee\xb4\x26\x2a\x5d\x3f\x8f\x52\x68\x55\x50\x5a\xde\x12\x95\x80\xa9\x10\x38\xd7\xec\x79\x42\xcd\x24\x09\xae\xab\x05\x8f\x40\xf4\xa3\xeb\x27\x00\x00\xff\xff\x68\xca\x16\xc2\xb4\x00\x00\x00") - -func templatesLayoutsLayoutHtmlBytes() ([]byte, error) { - return bindataRead( - _templatesLayoutsLayoutHtml, - "templates/layouts/layout.html", - ) -} - -func templatesLayoutsLayoutHtml() (*asset, error) { - bytes, err := templatesLayoutsLayoutHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "templates/layouts/layout.html", size: 180, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _templatesLayoutsMylayoutHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x34\x8f\x4d\x6a\xc5\x30\x0c\x84\xd7\x35\xf8\x0e\xd3\x03\x18\x93\xbd\xf1\x09\xba\x2a\xbd\x80\x53\xab\xc8\xe0\x9f\xe2\x28\x0b\x13\x72\xf7\x47\x9c\xbc\x95\x46\x62\x46\x7c\xe3\x58\x4a\xf6\x5a\x39\xa6\x10\xaf\x29\x49\x32\xf9\x32\xf0\x15\x46\xdb\xc5\xd9\xfb\xa0\x95\x56\xce\xbe\x4d\x6b\x8b\xc3\x6b\xf5\xe1\x78\xf1\x3f\x9c\x36\xa4\x0d\xc2\x84\x3c\x33\xf8\x6b\x7d\xae\xb6\x0c\x8b\x50\xe3\x14\x4d\x98\x3a\x7a\xdb\x85\x36\xb4\x9a\x87\xb3\xbc\xcc\x27\x6b\x87\x9d\xe2\xd3\x18\x7c\x53\x8d\x74\xc7\x7f\xf7\xde\xa9\x0a\x84\xca\x7f\x0e\x42\x60\xea\x04\x63\x2e\xef\x71\x60\x24\xca\x11\xe7\x79\x81\x3d\x40\xce\x3e\x75\x5e\x01\x00\x00\xff\xff\x64\xea\xc5\x1d\xd7\x00\x00\x00") - -func templatesLayoutsMylayoutHtmlBytes() ([]byte, error) { - return bindataRead( - _templatesLayoutsMylayoutHtml, - "templates/layouts/mylayout.html", - ) -} - -func templatesLayoutsMylayoutHtml() (*asset, error) { - bytes, err := templatesLayoutsMylayoutHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "templates/layouts/mylayout.html", size: 215, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _templatesPage1Html = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x3c\xca\x41\xaa\xc2\x30\x10\x00\xd0\xf5\x2f\xf4\x0e\xc3\xec\xbf\x25\x5b\x8d\x3d\x83\x37\x90\x69\x33\xa4\xa1\x63\x53\x26\x69\x40\x42\xee\x2e\xa2\xb8\x7c\xf0\xac\x0b\x05\x52\x7e\x0a\x5f\x71\xa2\x79\xf5\x1a\x8f\xcd\xfd\xcf\x51\xa2\x9e\x61\x12\x9a\xd7\x0b\xfc\x74\x30\x8e\x7d\xd7\x77\x7f\x76\x31\xe3\x8d\x3c\x83\x81\x5a\xc1\x2b\x73\x06\x0c\x1a\x12\x38\x2e\x2c\x71\x67\xc5\xd6\xec\xb0\x98\xcf\xaf\x15\x94\x37\xc7\x0a\xb8\x93\xe6\x40\x92\x86\x9d\x3c\x9b\xfb\x97\xe6\xb4\xe4\x87\x60\x6b\xef\x6e\x07\x17\xca\xd8\x77\xaf\x00\x00\x00\xff\xff\x47\x41\x4a\x5c\x9d\x00\x00\x00") - -func templatesPage1HtmlBytes() ([]byte, error) { - return bindataRead( - _templatesPage1Html, - "templates/page1.html", - ) -} - -func templatesPage1Html() (*asset, error) { - bytes, err := templatesPage1HtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "templates/page1.html", size: 157, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _templatesPartialsPage1_partial1Html = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\x49\xc9\x2c\x53\x28\x2e\xa9\xcc\x49\xb5\x55\x4a\x4a\x4c\xce\x4e\x2f\xca\x2f\xcd\x4b\xd1\x4d\xce\xcf\xc9\x2f\xb2\x52\x28\xcf\xc8\x2c\x49\xb5\x56\x80\xf2\x8a\x52\x53\x94\xec\x78\xb9\x38\x6d\x32\x0c\xed\x02\x12\xd3\x53\x15\x0c\xd5\x8b\x15\x02\x12\x8b\x4a\x32\x13\x73\x14\x0c\x6d\xf4\x33\x0c\xed\x78\xb9\x6c\xf4\x53\x32\xcb\xec\x78\xb9\x00\x01\x00\x00\xff\xff\xa2\xa6\x60\xb6\x59\x00\x00\x00") - -func templatesPartialsPage1_partial1HtmlBytes() ([]byte, error) { - return bindataRead( - _templatesPartialsPage1_partial1Html, - "templates/partials/page1_partial1.html", - ) -} - -func templatesPartialsPage1_partial1Html() (*asset, error) { - bytes, err := templatesPartialsPage1_partial1HtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "templates/partials/page1_partial1.html", size: 89, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "templates/layouts/layout.html": templatesLayoutsLayoutHtml, - "templates/layouts/mylayout.html": templatesLayoutsMylayoutHtml, - "templates/page1.html": templatesPage1Html, - "templates/partials/page1_partial1.html": templatesPartialsPage1_partial1Html, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"} -// AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("notexist") would return an error -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "templates": {nil, map[string]*bintree{ - "layouts": {nil, map[string]*bintree{ - "layout.html": {templatesLayoutsLayoutHtml, map[string]*bintree{}}, - "mylayout.html": {templatesLayoutsMylayoutHtml, map[string]*bintree{}}, - }}, - "page1.html": {templatesPage1Html, map[string]*bintree{}}, - "partials": {nil, map[string]*bintree{ - "page1_partial1.html": {templatesPartialsPage1_partial1Html, map[string]*bintree{}}, - }}, - }}, -}} - -// RestoreAsset restores an asset under the given directory -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil -} - -// RestoreAssets restores an asset under the given directory recursively -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) -} diff --git a/_examples/view/embedding-templates-into-app/main.go b/_examples/view/embedding-templates-into-app/main.go deleted file mode 100644 index da087a86d8..0000000000 --- a/_examples/view/embedding-templates-into-app/main.go +++ /dev/null @@ -1,61 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - tmpl := iris.HTML("./templates", ".html") - tmpl.Layout("layouts/layout.html") - tmpl.AddFunc("greet", func(s string) string { - return "Greetings " + s + "!" - }) - - // $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata - // $ go-bindata ./templates/... - // $ go run . - // html files are not used, you can delete the folder and run the example. - tmpl.Binary(Asset, AssetNames) // <-- IMPORTANT - - app.RegisterView(tmpl) - - app.Get("/", func(ctx iris.Context) { - if err := ctx.View("page1.html"); err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.Writef(err.Error()) - } - }) - - // remove the layout for a specific route - app.Get("/nolayout", func(ctx iris.Context) { - ctx.ViewLayout(iris.NoLayout) - if err := ctx.View("page1.html"); err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.Writef(err.Error()) - } - }) - - // set a layout for a party, .Layout should be BEFORE any Get or other Handle party's method - my := app.Party("/my").Layout("layouts/mylayout.html") - { // both of these will use the layouts/mylayout.html as their layout. - my.Get("/", func(ctx iris.Context) { - ctx.View("page1.html") - }) - my.Get("/other", func(ctx iris.Context) { - ctx.View("page1.html") - }) - } - - // http://localhost:8080 - // http://localhost:8080/nolayout - // http://localhost:8080/my - // http://localhost:8080/my/other - app.Listen(":8080") -} - -// Note for new Gophers: -// `go build` is used instead of `go run main.go` as the example comments says -// otherwise you will get compile errors, this is a Go thing; -// because you have multiple files in the `package main`. diff --git a/_examples/view/embedding-templates-into-app/templates/layouts/layout.html b/_examples/view/embedding-templates-into-app/templates/layouts/layout.html deleted file mode 100644 index eb543f0ed8..0000000000 --- a/_examples/view/embedding-templates-into-app/templates/layouts/layout.html +++ /dev/null @@ -1,12 +0,0 @@ - - -Layout - - - -

This is the global layout

-
- - {{ yield }} - - diff --git a/_examples/view/embedding-templates-into-app/templates/layouts/mylayout.html b/_examples/view/embedding-templates-into-app/templates/layouts/mylayout.html deleted file mode 100644 index d87575d3b9..0000000000 --- a/_examples/view/embedding-templates-into-app/templates/layouts/mylayout.html +++ /dev/null @@ -1,12 +0,0 @@ - - -my Layout - - - -

This is the layout for the /my/ and /my/other routes only

-
- - {{ yield }} - - diff --git a/_examples/view/embedding-templates-into-app/templates/page1.html b/_examples/view/embedding-templates-into-app/templates/page1.html deleted file mode 100644 index 22bd16a18e..0000000000 --- a/_examples/view/embedding-templates-into-app/templates/page1.html +++ /dev/null @@ -1,7 +0,0 @@ -
- -

Page 1 {{ greet "iris developer"}}

- - {{ render "partials/page1_partial1.html"}} - -
diff --git a/_examples/view/embedding-templates-into-app/templates/partials/page1_partial1.html b/_examples/view/embedding-templates-into-app/templates/partials/page1_partial1.html deleted file mode 100644 index 8af006d242..0000000000 --- a/_examples/view/embedding-templates-into-app/templates/partials/page1_partial1.html +++ /dev/null @@ -1,3 +0,0 @@ -
-

Page 1's Partial 1

-
diff --git a/_examples/view/herotemplate/README.md b/_examples/view/herotemplate/README.md deleted file mode 100644 index ff71e06767..0000000000 --- a/_examples/view/herotemplate/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Hero Template Example - -This folder contains the iris version of the original hero's example: https://github.com/shiyanhui/hero/tree/master/examples/app. - -Iris is 100% compatible with `net/http` so you don't have to change anything else -except the handler input from the original example. - -The only inline handler's changes were: - -From: - -```go -if _, err := w.Write(buffer.Bytes()); err != nil { -// and -template.UserListToWriter(userList, w) -``` -To: -```go -if _, err := ctx.Write(buffer.Bytes()); err != nil { -// and -template.UserListToWriter(userList, ctx) -``` - -So easy. - -Read more at: https://github.com/shiyanhui/hero \ No newline at end of file diff --git a/_examples/view/herotemplate/app.go b/_examples/view/herotemplate/app.go deleted file mode 100644 index 62f7a53ab3..0000000000 --- a/_examples/view/herotemplate/app.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "bytes" - - "github.com/kataras/iris/v12/_examples/view/herotemplate/template" - - "github.com/kataras/iris/v12" -) - -// $ go get -u github.com/shiyanhui/hero/hero -// $ go run app.go -// -// Read more at https://github.com/shiyanhui/hero/hero - -func main() { - app := iris.New() - - app.Get("/users", func(ctx iris.Context) { - ctx.CompressWriter(true) - ctx.ContentType("text/html") - - userList := []string{ - "Alice", - "Bob", - "Tom", - } - - // Had better use buffer sync.Pool. - // Hero(github.com/shiyanhui/hero/hero) exports GetBuffer and PutBuffer for this. - // - // buffer := hero.GetBuffer() - // defer hero.PutBuffer(buffer) - // buffer := new(bytes.Buffer) - // template.UserList(userList, buffer) - // ctx.Write(buffer.Bytes()) - - // iris context implements the io.Writer: - // _, err := template.UserListToWriter(userList, ctx) - // OR: - buffer := new(bytes.Buffer) - template.UserList(userList, buffer) - - _, err := ctx.Write(buffer.Bytes()) - if err != nil { - ctx.StopWithError(iris.StatusInternalServerError, err) - return - } - }) - - app.Listen(":8080") -} diff --git a/_examples/view/herotemplate/template/index.html b/_examples/view/herotemplate/template/index.html deleted file mode 100644 index c39ba0e786..0000000000 --- a/_examples/view/herotemplate/template/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - <%@ body { %> - <% } %> - - diff --git a/_examples/view/herotemplate/template/index.html.go b/_examples/view/herotemplate/template/index.html.go deleted file mode 100644 index da8b5842f8..0000000000 --- a/_examples/view/herotemplate/template/index.html.go +++ /dev/null @@ -1,3 +0,0 @@ -// Code generated by hero. -// DO NOT EDIT! -package template diff --git a/_examples/view/herotemplate/template/user.html b/_examples/view/herotemplate/template/user.html deleted file mode 100644 index 812fd4c5af..0000000000 --- a/_examples/view/herotemplate/template/user.html +++ /dev/null @@ -1,3 +0,0 @@ -
  • - <%= user %> -
  • diff --git a/_examples/view/herotemplate/template/user.html.go b/_examples/view/herotemplate/template/user.html.go deleted file mode 100644 index da8b5842f8..0000000000 --- a/_examples/view/herotemplate/template/user.html.go +++ /dev/null @@ -1,3 +0,0 @@ -// Code generated by hero. -// DO NOT EDIT! -package template diff --git a/_examples/view/herotemplate/template/userlist.html b/_examples/view/herotemplate/template/userlist.html deleted file mode 100644 index afb27d716c..0000000000 --- a/_examples/view/herotemplate/template/userlist.html +++ /dev/null @@ -1,11 +0,0 @@ -<%: func UserList(userList []string, buffer *bytes.Buffer) %> - -<%~ "index.html" %> - -<%@ body { %> - <% for _, user := range userList { %> -
      - <%+ "user.html" %> -
    - <% } %> -<% } %> diff --git a/_examples/view/herotemplate/template/userlist.html.go b/_examples/view/herotemplate/template/userlist.html.go deleted file mode 100644 index d8d62324de..0000000000 --- a/_examples/view/herotemplate/template/userlist.html.go +++ /dev/null @@ -1,40 +0,0 @@ -// Code generated by hero. -// DO NOT EDIT! -package template - -import ( - "bytes" - - "github.com/shiyanhui/hero" -) - -func UserList(userList []string, buffer *bytes.Buffer) { - buffer.WriteString(` - - - - - - - `) - for _, user := range userList { - buffer.WriteString(` -
      - `) - buffer.WriteString(`
    • - `) - hero.EscapeHTML(user, buffer) - buffer.WriteString(` -
    • -`) - - buffer.WriteString(` -
    - `) - } - - buffer.WriteString(` - - -`) -} diff --git a/_examples/view/herotemplate/template/userlistwriter.html b/_examples/view/herotemplate/template/userlistwriter.html deleted file mode 100644 index 59e68609bf..0000000000 --- a/_examples/view/herotemplate/template/userlistwriter.html +++ /dev/null @@ -1,11 +0,0 @@ -<%: func UserListToWriter(userList []string, w io.Writer) (int, error)%> - -<%~ "index.html" %> - -<%@ body { %> - <% for _, user := range userList { %> -
      - <%+ "user.html" %> -
    - <% } %> -<% } %> diff --git a/_examples/view/herotemplate/template/userlistwriter.html.go b/_examples/view/herotemplate/template/userlistwriter.html.go deleted file mode 100644 index f6df2ed55f..0000000000 --- a/_examples/view/herotemplate/template/userlistwriter.html.go +++ /dev/null @@ -1,43 +0,0 @@ -// Code generated by hero. -// DO NOT EDIT! -package template - -import ( - "io" - - "github.com/shiyanhui/hero" -) - -func UserListToWriter(userList []string, w io.Writer) (int, error) { - _buffer := hero.GetBuffer() - defer hero.PutBuffer(_buffer) - _buffer.WriteString(` - - - - - - - `) - for _, user := range userList { - _buffer.WriteString(` -
      - `) - _buffer.WriteString(`
    • - `) - hero.EscapeHTML(user, _buffer) - _buffer.WriteString(` -
    • -`) - - _buffer.WriteString(` -
    - `) - } - - _buffer.WriteString(` - - -`) - return w.Write(_buffer.Bytes()) -} diff --git a/_examples/view/overview/main.go b/_examples/view/overview/main.go deleted file mode 100644 index 8aeaabf1ad..0000000000 --- a/_examples/view/overview/main.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - - // with default template funcs: - // - // - {{ urlpath "mynamedroute" "pathParameter_ifneeded" }} - // - {{ render "header.html" }} - // - {{ render_r "header.html" }} // partial relative path to current page - // - {{ yield }} - // - {{ current }} - app.RegisterView(iris.HTML("./templates", ".html")) - app.Get("/", func(ctx iris.Context) { - ctx.CompressWriter(true) // enable compression based on Accept-Encoding (e.g. "gzip"). - ctx.ViewData("Name", "iris") // the .Name inside the ./templates/hi.html. - ctx.View("hi.html") // render the template with the file name relative to the './templates'. - }) - - // http://localhost:8080/ - app.Listen(":8080") -} - -/* -Note: - -In case you're wondering, the code behind the view engines derives from the "github.com/kataras/iris/v12/view" package, -access to the engines' variables can be granded by "github.com/kataras/iris/v12" package too. - - iris.HTML(...) is a shortcut of view.HTML(...) - iris.Django(...) >> >> view.Django(...) - iris.Pug(...) >> >> view.Pug(...) - iris.Handlebars(...) >> >> view.Handlebars(...) - iris.Amber(...) >> >> view.Amber(...) -*/ diff --git a/_examples/view/overview/templates/hi.html b/_examples/view/overview/templates/hi.html deleted file mode 100644 index aab59aa26d..0000000000 --- a/_examples/view/overview/templates/hi.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - Hi iris - - - -

    Hi {{.Name}}

    - - - \ No newline at end of file diff --git a/_examples/view/quicktemplate/README.md b/_examples/view/quicktemplate/README.md deleted file mode 100644 index 3d3a6f9b9e..0000000000 --- a/_examples/view/quicktemplate/README.md +++ /dev/null @@ -1,19 +0,0 @@ -First of all, install [quicktemplate](https://github.com/valyala/quicktemplate) package and [quicktemplate compiler](https://github.com/valyala/quicktemplate/tree/master/qtc) - -```sh -go get -u github.com/valyala/quicktemplate -go get -u github.com/valyala/quicktemplate/qtc -``` - -The example has the Go code compiled already for you, therefore: -```sh -go run main.go # http://localhost:8080 -``` - -However there is an instruction below, full documentation can be found at https://github.com/valyala/quicktemplate. - -Save your template files into `templates` folder under the extension *.qtpl, open your terminal and run `qtc` inside this folder. - -If all went ok, `*.qtpl.go` files must appear in the `templates` folder. These files contain the Go code for all `*.qtpl` files. - -> Remember, each time you change a a `/templates/*.qtpl` file you have to run the `qtc` command and re-build your application. \ No newline at end of file diff --git a/_examples/view/quicktemplate/controllers/execute_template.go b/_examples/view/quicktemplate/controllers/execute_template.go deleted file mode 100644 index 6baf71a4f2..0000000000 --- a/_examples/view/quicktemplate/controllers/execute_template.go +++ /dev/null @@ -1,14 +0,0 @@ -package controllers - -import ( - "github.com/kataras/iris/v12/_examples/view/quicktemplate/templates" - - "github.com/kataras/iris/v12" -) - -// ExecuteTemplate renders a "tmpl" partial template to the `Context.ResponseWriter`. -func ExecuteTemplate(ctx iris.Context, tmpl templates.Partial) { - ctx.CompressWriter(true) - ctx.ContentType("text/html") - templates.WriteTemplate(ctx, tmpl) -} diff --git a/_examples/view/quicktemplate/controllers/hello.go b/_examples/view/quicktemplate/controllers/hello.go deleted file mode 100644 index 009742a406..0000000000 --- a/_examples/view/quicktemplate/controllers/hello.go +++ /dev/null @@ -1,30 +0,0 @@ -package controllers - -import ( - "github.com/kataras/iris/v12/_examples/view/quicktemplate/templates" - - "github.com/kataras/iris/v12" -) - -// Hello renders our ../templates/hello.qtpl file using the compiled ../templates/hello.qtpl.go file. -func Hello(ctx iris.Context) { - // vars := make(map[string]interface{}) - // vars["message"] = "Hello World!" - // vars["name"] = ctx.Params().Get("name") - // [...] - // &templates.Hello{ Vars: vars } - // [...] - - // However, as an alternative, we recommend that you should the `ctx.ViewData(key, value)` - // in order to be able modify the `templates.Hello#Vars` from a middleware(other handlers) as well. - ctx.ViewData("message", "Hello World!") - ctx.ViewData("name", ctx.Params().Get("name")) - - // set view data to the `Vars` template's field - tmpl := &templates.Hello{ - Vars: ctx.GetViewData(), - } - - // render the template - ExecuteTemplate(ctx, tmpl) -} diff --git a/_examples/view/quicktemplate/controllers/index.go b/_examples/view/quicktemplate/controllers/index.go deleted file mode 100644 index 56cf6717ea..0000000000 --- a/_examples/view/quicktemplate/controllers/index.go +++ /dev/null @@ -1,15 +0,0 @@ -package controllers - -import ( - "github.com/kataras/iris/v12/_examples/view/quicktemplate/templates" - - "github.com/kataras/iris/v12" -) - -// Index renders our ../templates/index.qtpl file using the compiled ../templates/index.qtpl.go file. -func Index(ctx iris.Context) { - tmpl := &templates.Index{} - - // render the template - ExecuteTemplate(ctx, tmpl) -} diff --git a/_examples/view/quicktemplate/main.go b/_examples/view/quicktemplate/main.go deleted file mode 100644 index 7a63ab0d4c..0000000000 --- a/_examples/view/quicktemplate/main.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12/_examples/view/quicktemplate/controllers" - - "github.com/kataras/iris/v12" -) - -func newApp() *iris.Application { - app := iris.New() - app.Get("/", controllers.Index) - app.Get("/{name}", controllers.Hello) - - return app -} - -func main() { - app := newApp() - // http://localhost:8080 - // http://localhost:8080/yourname - app.Listen(":8080") -} diff --git a/_examples/view/quicktemplate/main_test.go b/_examples/view/quicktemplate/main_test.go deleted file mode 100644 index 5fcf941187..0000000000 --- a/_examples/view/quicktemplate/main_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "fmt" - "testing" - - "github.com/kataras/iris/v12/httptest" -) - -func TestResponseWriterQuicktemplate(t *testing.T) { - baseRawBody := ` - - - Quicktemplate integration with Iris - - -
    - Header contents here... -
    - -
    - -

    %s

    -
    - %s -
    - -
    - - -
    - Footer contents here... -
    - -` - - expectedIndexRawBody := fmt.Sprintf(baseRawBody, "Index Page", "This is our index page's body.") - name := "yourname" - expectedHelloRawBody := fmt.Sprintf(baseRawBody, "Hello World!", "Hello "+name+"!") - - app := newApp() - - e := httptest.New(t, app) - - e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal(expectedIndexRawBody) - e.GET("/" + name).Expect().Status(httptest.StatusOK).Body().Equal(expectedHelloRawBody) -} diff --git a/_examples/view/quicktemplate/models/.gitkeep b/_examples/view/quicktemplate/models/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/_examples/view/quicktemplate/templates/base.qtpl b/_examples/view/quicktemplate/templates/base.qtpl deleted file mode 100644 index 4e3fd92371..0000000000 --- a/_examples/view/quicktemplate/templates/base.qtpl +++ /dev/null @@ -1,36 +0,0 @@ -This is our templates' base implementation. - -{% interface -Partial { - Body() -} -%} - - -Template writes a template implementing the Partial interface. -{% func Template(p Partial) %} - - - Quicktemplate integration with Iris - - -
    - Header contents here... -
    - -
    - {%= p.Body() %} -
    - - -
    - Footer contents here... -
    - -{% endfunc %} - - -Base template implementation. Other pages may inherit from it if they need -overriding only certain Partial methods. -{% code type Base struct {} %} -{% func (b *Base) Body() %}This is the base body{% endfunc %} diff --git a/_examples/view/quicktemplate/templates/base.qtpl.go b/_examples/view/quicktemplate/templates/base.qtpl.go deleted file mode 100644 index d1e988f1ef..0000000000 --- a/_examples/view/quicktemplate/templates/base.qtpl.go +++ /dev/null @@ -1,147 +0,0 @@ -// This file is automatically generated by qtc from "base.qtpl". -// See https://github.com/valyala/quicktemplate for details. - -// This is our templates' base implementation. -// - -//line base.qtpl:3 - -package templates - -//line base.qtpl:3 - -import ( - qtio422016 "io" - - qt422016 "github.com/valyala/quicktemplate" -) - -//line base.qtpl:3 - -var ( - _ = qtio422016.Copy - _ = qt422016.AcquireByteBuffer -) - -//line base.qtpl:4 - -type Partial interface { - //line base.qtpl:4 - Body() string - //line base.qtpl:4 - StreamBody(qw422016 *qt422016.Writer) - //line base.qtpl:4 - WriteBody(qq422016 qtio422016.Writer) -//line base.qtpl:4 - -} - -// Template writes a template implementing the Partial interface. - -//line base.qtpl:11 - -func StreamTemplate(qw422016 *qt422016.Writer, p Partial) { - //line base.qtpl:11 - qw422016.N().S(` - - - Quicktemplate integration with Iris - - -
    - Header contents here... -
    - -
    - `) - //line base.qtpl:22 - p.StreamBody(qw422016) - //line base.qtpl:22 - qw422016.N().S(` -
    - - -
    - Footer contents here... -
    - -`) -//line base.qtpl:30 - -} - -//line base.qtpl:30 - -func WriteTemplate(qq422016 qtio422016.Writer, p Partial) { - //line base.qtpl:30 - qw422016 := qt422016.AcquireWriter(qq422016) - //line base.qtpl:30 - StreamTemplate(qw422016, p) - //line base.qtpl:30 - qt422016.ReleaseWriter(qw422016) -//line base.qtpl:30 - -} - -//line base.qtpl:30 - -func Template(p Partial) string { - //line base.qtpl:30 - qb422016 := qt422016.AcquireByteBuffer() - //line base.qtpl:30 - WriteTemplate(qb422016, p) - //line base.qtpl:30 - qs422016 := string(qb422016.B) - //line base.qtpl:30 - qt422016.ReleaseByteBuffer(qb422016) - //line base.qtpl:30 - return qs422016 -//line base.qtpl:30 - -} - -// Base template implementation. Other pages may inherit from it if they need -// overriding only certain Partial methods. - -//line base.qtpl:35 - -type Base struct{} - -//line base.qtpl:36 - -func (b *Base) StreamBody(qw422016 *qt422016.Writer) { -//line base.qtpl:36 - - qw422016.N().S(`This is the base body`) -} - -//line base.qtpl:36 -//line base.qtpl:36 - -func (b *Base) WriteBody(qq422016 qtio422016.Writer) { - //line base.qtpl:36 - qw422016 := qt422016.AcquireWriter(qq422016) - //line base.qtpl:36 - b.StreamBody(qw422016) - //line base.qtpl:36 - qt422016.ReleaseWriter(qw422016) -//line base.qtpl:36 - -} - -//line base.qtpl:36 - -func (b *Base) Body() string { - //line base.qtpl:36 - qb422016 := qt422016.AcquireByteBuffer() - //line base.qtpl:36 - b.WriteBody(qb422016) - //line base.qtpl:36 - qs422016 := string(qb422016.B) - //line base.qtpl:36 - qt422016.ReleaseByteBuffer(qb422016) - //line base.qtpl:36 - return qs422016 -//line base.qtpl:36 - -} diff --git a/_examples/view/quicktemplate/templates/hello.qtpl b/_examples/view/quicktemplate/templates/hello.qtpl deleted file mode 100644 index 9752cc8d7b..0000000000 --- a/_examples/view/quicktemplate/templates/hello.qtpl +++ /dev/null @@ -1,14 +0,0 @@ -Hello template, implements the Partial's methods. - -{% code -type Hello struct { - Vars map[string]interface{} -} -%} - -{% func (h *Hello) Body() %} -

    {%v h.Vars["message"] %}

    -
    - Hello {%v h.Vars["name"] %}! -
    -{% endfunc %} diff --git a/_examples/view/quicktemplate/templates/hello.qtpl.go b/_examples/view/quicktemplate/templates/hello.qtpl.go deleted file mode 100644 index 61db387a46..0000000000 --- a/_examples/view/quicktemplate/templates/hello.qtpl.go +++ /dev/null @@ -1,82 +0,0 @@ -// This file is automatically generated by qtc from "hello.qtpl". -// See https://github.com/valyala/quicktemplate for details. - -// Hello template, implements the Partial's methods. -// - -//line hello.qtpl:3 - -package templates - -//line hello.qtpl:3 - -import ( - qtio422016 "io" - - qt422016 "github.com/valyala/quicktemplate" -) - -//line hello.qtpl:3 - -var ( - _ = qtio422016.Copy - _ = qt422016.AcquireByteBuffer -) - -//line hello.qtpl:4 - -type Hello struct { - Vars map[string]interface{} -} - -//line hello.qtpl:9 - -func (h *Hello) StreamBody(qw422016 *qt422016.Writer) { - //line hello.qtpl:9 - qw422016.N().S(` -

    `) - //line hello.qtpl:10 - qw422016.E().V(h.Vars["message"]) - //line hello.qtpl:10 - qw422016.N().S(`

    -
    - Hello `) - //line hello.qtpl:12 - qw422016.E().V(h.Vars["name"]) - //line hello.qtpl:12 - qw422016.N().S(`! -
    -`) -//line hello.qtpl:14 - -} - -//line hello.qtpl:14 - -func (h *Hello) WriteBody(qq422016 qtio422016.Writer) { - //line hello.qtpl:14 - qw422016 := qt422016.AcquireWriter(qq422016) - //line hello.qtpl:14 - h.StreamBody(qw422016) - //line hello.qtpl:14 - qt422016.ReleaseWriter(qw422016) -//line hello.qtpl:14 - -} - -//line hello.qtpl:14 - -func (h *Hello) Body() string { - //line hello.qtpl:14 - qb422016 := qt422016.AcquireByteBuffer() - //line hello.qtpl:14 - h.WriteBody(qb422016) - //line hello.qtpl:14 - qs422016 := string(qb422016.B) - //line hello.qtpl:14 - qt422016.ReleaseByteBuffer(qb422016) - //line hello.qtpl:14 - return qs422016 -//line hello.qtpl:14 - -} diff --git a/_examples/view/quicktemplate/templates/index.qtpl b/_examples/view/quicktemplate/templates/index.qtpl deleted file mode 100644 index f25512970b..0000000000 --- a/_examples/view/quicktemplate/templates/index.qtpl +++ /dev/null @@ -1,12 +0,0 @@ -Index template, implements the Partial's methods. - -{% code -type Index struct {} -%} - -{% func (i *Index) Body() %} -

    Index Page

    -
    - This is our index page's body. -
    -{% endfunc %} diff --git a/_examples/view/quicktemplate/templates/index.qtpl.go b/_examples/view/quicktemplate/templates/index.qtpl.go deleted file mode 100644 index 18b4824d5e..0000000000 --- a/_examples/view/quicktemplate/templates/index.qtpl.go +++ /dev/null @@ -1,72 +0,0 @@ -// This file is automatically generated by qtc from "index.qtpl". -// See https://github.com/valyala/quicktemplate for details. - -// Index template, implements the Partial's methods. -// - -//line index.qtpl:3 - -package templates - -//line index.qtpl:3 - -import ( - qtio422016 "io" - - qt422016 "github.com/valyala/quicktemplate" -) - -//line index.qtpl:3 - -var ( - _ = qtio422016.Copy - _ = qt422016.AcquireByteBuffer -) - -//line index.qtpl:4 - -type Index struct{} - -//line index.qtpl:7 - -func (i *Index) StreamBody(qw422016 *qt422016.Writer) { - //line index.qtpl:7 - qw422016.N().S(` -

    Index Page

    -
    - This is our index page's body. -
    -`) -//line index.qtpl:12 - -} - -//line index.qtpl:12 - -func (i *Index) WriteBody(qq422016 qtio422016.Writer) { - //line index.qtpl:12 - qw422016 := qt422016.AcquireWriter(qq422016) - //line index.qtpl:12 - i.StreamBody(qw422016) - //line index.qtpl:12 - qt422016.ReleaseWriter(qw422016) -//line index.qtpl:12 - -} - -//line index.qtpl:12 - -func (i *Index) Body() string { - //line index.qtpl:12 - qb422016 := qt422016.AcquireByteBuffer() - //line index.qtpl:12 - i.WriteBody(qb422016) - //line index.qtpl:12 - qs422016 := string(qb422016.B) - //line index.qtpl:12 - qt422016.ReleaseByteBuffer(qb422016) - //line index.qtpl:12 - return qs422016 -//line index.qtpl:12 - -} diff --git a/_examples/view/template_ace_0/main.go b/_examples/view/template_ace_0/main.go deleted file mode 100644 index fcf6619b4c..0000000000 --- a/_examples/view/template_ace_0/main.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - - // Read about its markup syntax at: https://github.com/yosssi/ace - tmpl := iris.Ace("./views", ".ace") - // tmpl.Layout("layouts/main.ace") -> global layout for all pages. - - app.RegisterView(tmpl) - - app.Get("/", func(ctx iris.Context) { - ctx.View("index.ace", iris.Map{ - "Title": "Title of The Page", - }) - }) - - app.Get("/layout", func(ctx iris.Context) { - ctx.ViewLayout("layouts/main.ace") // layout for that response. - ctx.View("index.ace", iris.Map{ - "Title": "Title of the main Page", - }) - }) - - // otherGroup := app.Party("/other").Layout("layouts/other.ace") -> layout for that party. - // otherGroup.Get("/", func(ctx iris.Context) { ctx.View("index.ace", [...]) }) - - app.Listen(":8080") -} diff --git a/_examples/view/template_ace_0/views/index.ace b/_examples/view/template_ace_0/views/index.ace deleted file mode 100644 index 776cc11ff8..0000000000 --- a/_examples/view/template_ace_0/views/index.ace +++ /dev/null @@ -1,5 +0,0 @@ -= include partials/header.ace . - -h2 {{.Title}} - -= include partials/footer.ace . \ No newline at end of file diff --git a/_examples/view/template_ace_0/views/layouts/main.ace b/_examples/view/template_ace_0/views/layouts/main.ace deleted file mode 100644 index b3997c7cce..0000000000 --- a/_examples/view/template_ace_0/views/layouts/main.ace +++ /dev/null @@ -1,6 +0,0 @@ -= doctype html -html - head - title Main Page - body - {{ yield }} \ No newline at end of file diff --git a/_examples/view/template_ace_0/views/partials/footer.ace b/_examples/view/template_ace_0/views/partials/footer.ace deleted file mode 100644 index 9926168518..0000000000 --- a/_examples/view/template_ace_0/views/partials/footer.ace +++ /dev/null @@ -1 +0,0 @@ -h1 Partial Footer \ No newline at end of file diff --git a/_examples/view/template_ace_0/views/partials/header.ace b/_examples/view/template_ace_0/views/partials/header.ace deleted file mode 100644 index dca23ac258..0000000000 --- a/_examples/view/template_ace_0/views/partials/header.ace +++ /dev/null @@ -1 +0,0 @@ -h1 Partial Header \ No newline at end of file diff --git a/_examples/view/template_blocks_0/main.go b/_examples/view/template_blocks_0/main.go deleted file mode 100644 index 8e46a50696..0000000000 --- a/_examples/view/template_blocks_0/main.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - // Read about its syntax at: https://github.com/kataras/blocks - app.RegisterView(iris.Blocks("./views", ".html").Reload(true)) - - app.Get("/", index) - app.Get("/500", internalServerError) - - app.Listen(":8080") -} - -func index(ctx iris.Context) { - data := iris.Map{ - "Title": "Page Title", - } - - ctx.ViewLayout("main") - ctx.View("index", data) -} - -func internalServerError(ctx iris.Context) { - ctx.StatusCode(iris.StatusInternalServerError) - - data := iris.Map{ - "Code": iris.StatusInternalServerError, - "Message": "Internal Server Error", - } - - ctx.ViewLayout("error") - ctx.View("500", data) -} diff --git a/_examples/view/template_blocks_0/views/500.html b/_examples/view/template_blocks_0/views/500.html deleted file mode 100644 index 37c58b5884..0000000000 --- a/_examples/view/template_blocks_0/views/500.html +++ /dev/null @@ -1,12 +0,0 @@ - -{{ define "content" }} -

    Internal Server Error

    -{{ end }} - -{{ define "message" }} -

    {{.Message}}

    -{{ end }} \ No newline at end of file diff --git a/_examples/view/template_blocks_0/views/index.html b/_examples/view/template_blocks_0/views/index.html deleted file mode 100644 index b11758cf1c..0000000000 --- a/_examples/view/template_blocks_0/views/index.html +++ /dev/null @@ -1 +0,0 @@ -

    Index Body

    \ No newline at end of file diff --git a/_examples/view/template_blocks_0/views/layouts/error.html b/_examples/view/template_blocks_0/views/layouts/error.html deleted file mode 100644 index 48598789db..0000000000 --- a/_examples/view/template_blocks_0/views/layouts/error.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - {{.Code}} - - - {{ template "content" .}} - - {{block "message" .}}{{end}} - - \ No newline at end of file diff --git a/_examples/view/template_blocks_0/views/layouts/main.html b/_examples/view/template_blocks_0/views/layouts/main.html deleted file mode 100644 index 5b5a509f4c..0000000000 --- a/_examples/view/template_blocks_0/views/layouts/main.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - {{ if .Title }}{{ .Title }}{{ else }}Default Main Title{{ end }} - - - {{ template "content" . }} - -
    {{ partial "partials/footer" .}}
    - - \ No newline at end of file diff --git a/_examples/view/template_blocks_0/views/partials/footer.html b/_examples/view/template_blocks_0/views/partials/footer.html deleted file mode 100644 index 61ee8461de..0000000000 --- a/_examples/view/template_blocks_0/views/partials/footer.html +++ /dev/null @@ -1 +0,0 @@ -

    Footer Partial

    \ No newline at end of file diff --git a/_examples/view/template_blocks_1_embedded/bindata.go b/_examples/view/template_blocks_1_embedded/bindata.go deleted file mode 100644 index 465eb47ce1..0000000000 --- a/_examples/view/template_blocks_1_embedded/bindata.go +++ /dev/null @@ -1,343 +0,0 @@ -// Code generated by go-bindata. (@generated) DO NOT EDIT. - -//Package main generated by go-bindata.// sources: -// ../template_blocks_0/views/500.html -// ../template_blocks_0/views/index.html -// ../template_blocks_0/views/layouts/error.html -// ../template_blocks_0/views/layouts/main.html -// ../template_blocks_0/views/partials/footer.html -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data []byte, name string) ([]byte, error) { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -// Name return file name -func (fi bindataFileInfo) Name() string { - return fi.name -} - -// Size return file size -func (fi bindataFileInfo) Size() int64 { - return fi.size -} - -// Mode return file mode -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} - -// ModTime return file modify time -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} - -// IsDir return file whether a directory -func (fi bindataFileInfo) IsDir() bool { - return fi.mode&os.ModeDir != 0 -} - -// Sys return file is sys mode -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -var _views500Html = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\x8f\xb1\x4e\xf3\x30\x14\x85\xf7\x48\x79\x87\xfb\x67\xe9\x8f\xd4\xa4\xea\x5a\x42\x37\x06\x06\xa6\x22\x21\x46\xc7\x3e\xad\x2d\x9c\x7b\x23\xdb\x49\x89\xa2\xbc\x3b\x6a\x5a\x04\x2c\xac\xf7\x7e\xe7\x3b\x3a\xf5\xbf\xb2\xa4\x37\xe9\x49\x2b\x26\x83\xa3\x63\x50\x2b\x01\x94\xac\x62\x12\x06\x35\x5e\xf4\x7b\x95\x67\x2f\x16\x17\x40\xf5\x3e\x2d\x77\x17\xa9\xd0\xc2\x09\x9c\x0a\x3a\x5b\xa7\x2d\x45\x2b\xbd\x37\xd4\x5c\xd2\xa0\x56\x39\xa6\x84\xb6\xf3\x2a\x61\x15\xa9\x11\x33\x56\x79\x76\x90\x35\x61\x00\x93\x3b\x92\x4b\xab\x48\xad\x8b\xd1\xf1\x89\xfe\x47\x80\x1c\x1b\x7c\x54\x36\xb5\xfe\x6e\x7d\x7d\x2b\x63\x60\x48\xf5\x49\x5a\x95\x9c\x56\xde\x8f\xd4\x8c\x4b\xc3\xe0\x70\x26\xf0\xc9\x31\xaa\x3c\x7b\xb5\x60\x1a\xa5\x27\x06\x0c\x25\xf9\x63\xce\x7a\xe1\xac\x1a\x70\xe1\x9a\x1b\x13\x3b\x68\x77\x74\x7a\x97\x67\x65\xb9\xcf\xb3\x69\xfa\x52\x7c\x0f\x9d\xe7\x3c\xab\xed\x76\xff\xc4\x09\x81\x95\xa7\x03\xc2\x80\x40\x8f\x21\x48\xa8\x37\x76\x7b\xcd\x81\xcd\x82\xfe\x92\xb4\x88\x51\x9d\x70\x93\x74\x14\xd3\xe8\xf1\x50\x68\xf1\x12\x76\x01\xe6\xbe\xd8\x4f\x53\xf5\x7c\xa5\xe6\xb9\xde\x74\x3f\x65\x9f\x01\x00\x00\xff\xff\xb3\xfa\x91\xbd\xaa\x01\x00\x00") - -func views500HtmlBytes() ([]byte, error) { - return bindataRead( - _views500Html, - "views/500.html", - ) -} - -func views500Html() (*asset, error) { - bytes, err := views500HtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "views/500.html", size: 426, mode: os.FileMode(438), modTime: time.Unix(1596515591, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _viewsIndexHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\xc9\x30\xb4\xf3\xcc\x4b\x49\xad\x50\x70\xca\x4f\xa9\xb4\xd1\xcf\x30\xb4\x03\x04\x00\x00\xff\xff\xcc\x4b\x98\x69\x13\x00\x00\x00") - -func viewsIndexHtmlBytes() ([]byte, error) { - return bindataRead( - _viewsIndexHtml, - "views/index.html", - ) -} - -func viewsIndexHtml() (*asset, error) { - bytes, err := viewsIndexHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "views/index.html", size: 19, mode: os.FileMode(438), modTime: time.Unix(1596514225, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _viewsLayoutsErrorHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x90\xbf\x4e\xf3\x40\x10\xc4\x7b\x4b\x7e\x87\xfd\xb6\xfe\x6c\x43\x47\x71\xe7\x26\x40\x0b\x45\x28\x28\x37\x77\xa3\xf8\xc4\xfd\x89\xe2\x55\x22\x74\xf2\xbb\xa3\x18\x83\x44\xb5\xda\x99\x9f\x66\x57\x63\xfe\x3d\xbe\xec\xf6\xef\xaf\x4f\x34\x69\x8a\x63\xdb\x98\xdb\xa4\x28\xf9\x68\x19\x99\x57\x05\xe2\xc7\xb6\x21\x22\x32\x09\x2a\xe4\x26\x39\xcf\x50\xcb\x6f\xfb\xe7\xee\x81\xff\x78\x59\x12\x2c\x5f\x02\xae\xa7\x72\x56\x26\x57\xb2\x22\xab\xe5\x6b\xf0\x3a\x59\x8f\x4b\x70\xe8\xd6\xe5\x3f\x85\x1c\x34\x48\xec\x66\x27\x11\xf6\xbe\xbf\xfb\xcd\xd2\xa0\x11\x63\xad\xfd\xae\x78\x2c\x8b\x19\xbe\x85\xb6\x31\xc3\xf6\x8e\x39\x14\xff\xb9\xe1\xb5\x92\x22\x9d\xa2\x28\x88\xb7\x8b\x4c\xfd\xb2\xb4\xcd\x0f\x70\x88\xc5\x7d\x10\x27\xcc\xb3\x1c\xb1\x9a\xb5\x22\xfb\x1b\x63\x86\x2d\xcb\x0c\x6b\x0b\x5f\x01\x00\x00\xff\xff\xbe\xb7\x11\x67\x15\x01\x00\x00") - -func viewsLayoutsErrorHtmlBytes() ([]byte, error) { - return bindataRead( - _viewsLayoutsErrorHtml, - "views/layouts/error.html", - ) -} - -func viewsLayoutsErrorHtml() (*asset, error) { - bytes, err := viewsLayoutsErrorHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "views/layouts/error.html", size: 277, mode: os.FileMode(438), modTime: time.Unix(1596514340, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _viewsLayoutsMainHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x90\xb1\x4e\xf4\x30\x10\x84\xfb\x48\x79\x87\xf9\x5d\xff\x49\xa0\xa3\xb0\xd3\x70\xd0\x21\x28\x42\x41\xb9\x24\x1b\x62\xc9\x71\xa2\x64\xb9\x13\xb2\xfc\xee\xc8\x39\x0b\xe9\x2a\xcf\xfa\xb3\x66\xc6\xab\xff\x9d\x5e\x1f\xbb\x8f\xb7\x27\x4c\x32\xbb\xb6\x2c\x74\x3a\xe1\xc8\x7f\x19\xc5\x5e\x1d\x37\x4c\x43\x5b\x16\x00\xa0\x67\x16\x42\x3f\xd1\xb6\xb3\x18\xf5\xde\x3d\x57\x0f\xea\x86\x79\x9a\xd9\xa8\xb3\xe5\xcb\xba\x6c\xa2\xd0\x2f\x5e\xd8\x8b\x51\x17\x3b\xc8\x64\x06\x3e\xdb\x9e\xab\x63\xf8\x0f\xeb\xad\x58\x72\xd5\xde\x93\x63\x73\x5f\xdf\xfd\x79\x89\x15\xc7\x6d\x08\xb0\x23\xea\x2e\x0d\x88\x31\x84\x1b\xcd\x6e\x4f\xea\xc4\x23\x7d\x3b\xc1\x0b\x59\x8f\x03\x27\xe6\x07\xc4\xa8\x9b\xab\x4f\x59\xe8\x26\xff\x42\x7f\x2e\xc3\x4f\x4e\x09\x01\xc2\xf3\xea\x48\x18\x2a\x17\x55\xa8\x11\x63\x59\x94\x85\x1e\x97\x45\x78\x4b\x25\x56\xda\x52\x4f\xa8\x2c\xf6\xe6\xca\x14\xea\x14\x92\x1f\xa6\x94\xec\xae\x9b\x63\x9d\xbf\x01\x00\x00\xff\xff\x44\x95\x63\x98\x5e\x01\x00\x00") - -func viewsLayoutsMainHtmlBytes() ([]byte, error) { - return bindataRead( - _viewsLayoutsMainHtml, - "views/layouts/main.html", - ) -} - -func viewsLayoutsMainHtml() (*asset, error) { - bytes, err := viewsLayoutsMainHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "views/layouts/main.html", size: 350, mode: os.FileMode(438), modTime: time.Unix(1596514155, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _viewsPartialsFooterHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\xc9\x30\xb6\x73\xcb\xcf\x2f\x49\x2d\x52\x08\x48\x2c\x2a\xc9\x4c\xcc\xb1\xd1\xcf\x30\xb6\x03\x04\x00\x00\xff\xff\x08\xe6\xe9\xf8\x17\x00\x00\x00") - -func viewsPartialsFooterHtmlBytes() ([]byte, error) { - return bindataRead( - _viewsPartialsFooterHtml, - "views/partials/footer.html", - ) -} - -func viewsPartialsFooterHtml() (*asset, error) { - bytes, err := viewsPartialsFooterHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "views/partials/footer.html", size: 23, mode: os.FileMode(438), modTime: time.Unix(1596514093, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "views/500.html": views500Html, - "views/index.html": viewsIndexHtml, - "views/layouts/error.html": viewsLayoutsErrorHtml, - "views/layouts/main.html": viewsLayoutsMainHtml, - "views/partials/footer.html": viewsPartialsFooterHtml, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"} -// AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("notexist") would return an error -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "views": {nil, map[string]*bintree{ - "500.html": {views500Html, map[string]*bintree{}}, - "index.html": {viewsIndexHtml, map[string]*bintree{}}, - "layouts": {nil, map[string]*bintree{ - "error.html": {viewsLayoutsErrorHtml, map[string]*bintree{}}, - "main.html": {viewsLayoutsMainHtml, map[string]*bintree{}}, - }}, - "partials": {nil, map[string]*bintree{ - "footer.html": {viewsPartialsFooterHtml, map[string]*bintree{}}, - }}, - }}, -}} - -// RestoreAsset restores an asset under the given directory -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil -} - -// RestoreAssets restores an asset under the given directory recursively -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) -} diff --git a/_examples/view/template_blocks_1_embedded/main.go b/_examples/view/template_blocks_1_embedded/main.go deleted file mode 100644 index 020a22357f..0000000000 --- a/_examples/view/template_blocks_1_embedded/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata -// $ go-bindata -prefix "../template_blocks_0" ../template_blocks_0/views/... -// $ go run . -// # OR go-bindata -prefix "../template_blocks_0/views" ../template_blocks_0/views/... with iris.Blocks("").Binary(...) -// System files are not used, you can optionally delete the folder and run the example now. - -func main() { - app := iris.New() - app.RegisterView(iris.Blocks("./views", ".html").Binary(Asset, AssetNames)) - - app.Get("/", index) - app.Get("/500", internalServerError) - - app.Listen(":8080") -} - -func index(ctx iris.Context) { - data := iris.Map{ - "Title": "Page Title", - } - - ctx.ViewLayout("main") - ctx.View("index", data) -} - -func internalServerError(ctx iris.Context) { - ctx.StatusCode(iris.StatusInternalServerError) - - data := iris.Map{ - "Code": iris.StatusInternalServerError, - "Message": "Internal Server Error", - } - - ctx.ViewLayout("error") - ctx.View("500", data) -} diff --git a/_examples/view/template_django_0/main.go b/_examples/view/template_django_0/main.go deleted file mode 100644 index 72dc256dc4..0000000000 --- a/_examples/view/template_django_0/main.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "time" - - "github.com/kataras/iris/v12" - - // optionally, registers filters like `timesince`. - _ "github.com/iris-contrib/pongo2-addons" -) - -var startTime = time.Now() - -func main() { - app := iris.New() - - tmpl := iris.Django("./templates", ".html") - tmpl.Reload(true) // reload templates on each request (development mode) - tmpl.AddFunc("greet", func(s string) string { // {{greet(name)}} - return "Greetings " + s + "!" - }) - - // tmpl.RegisterFilter("myFilter", myFilter) // {{"simple input for filter"|myFilter}} - app.RegisterView(tmpl) - - app.Get("/", hi) - - // http://localhost:8080 - app.Listen(":8080") -} - -func hi(ctx iris.Context) { - // ctx.ViewData("title", "Hi Page") - // ctx.ViewData("name", "iris") - // ctx.ViewData("serverStartTime", startTime) - // or if you set all view data in the same handler you can use the - // iris.Map/pongo2.Context/map[string]interface{}, look below: - - ctx.View("hi.html", iris.Map{ - "title": "Hi Page", - "name": "iris", - "serverStartTime": startTime, - }) -} diff --git a/_examples/view/template_django_0/templates/hi.html b/_examples/view/template_django_0/templates/hi.html deleted file mode 100644 index 945782e4b4..0000000000 --- a/_examples/view/template_django_0/templates/hi.html +++ /dev/null @@ -1,12 +0,0 @@ - - -{{title}} - - -

    Hi {{name|capfirst}}

    - -

    {{greet(name)}}

    - -

    Server started about {{serverStartTime|timesince}}. Refresh the page to see different result

    - - diff --git a/_examples/view/template_html_0/main.go b/_examples/view/template_html_0/main.go deleted file mode 100644 index ead5ec5636..0000000000 --- a/_examples/view/template_html_0/main.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() // defaults to these - - tmpl := iris.HTML("./templates", ".html") - tmpl.Reload(true) // reload templates on each request (development mode) - // default template funcs are: - // - // - {{ urlpath "mynamedroute" "pathParameter_ifneeded" }} - // - {{ render "header.html" }} - // - {{ render_r "header.html" }} // partial relative path to current page - // - {{ yield }} - // - {{ current }} - tmpl.AddFunc("greet", func(s string) string { - return "Greetings " + s + "!" - }) - app.RegisterView(tmpl) - - app.Get("/", hi) - - // http://localhost:8080 - app.Listen(":8080", iris.WithCharset("utf-8")) // defaults to that but you can change it. -} - -func hi(ctx iris.Context) { - ctx.ViewData("Title", "Hi Page") - ctx.ViewData("Name", "iris") // {{.Name}} will render: iris - // ctx.ViewData("", myCcustomStruct{}) - ctx.View("hi.html") -} - -/* -Note: - -In case you're wondering, the code behind the view engines derives from the "github.com/kataras/iris/v12/view" package, -access to the engines' variables can be granded by "github.com/kataras/iris/v12" package too. - - iris.HTML(...) is a shortcut of view.HTML(...) - iris.Django(...) >> >> view.Django(...) - iris.Pug(...) >> >> view.Pug(...) - iris.Handlebars(...) >> >> view.Handlebars(...) - iris.Amber(...) >> >> view.Amber(...) -*/ diff --git a/_examples/view/template_html_0/templates/hi.html b/_examples/view/template_html_0/templates/hi.html deleted file mode 100644 index fe355ba19d..0000000000 --- a/_examples/view/template_html_0/templates/hi.html +++ /dev/null @@ -1,8 +0,0 @@ - - -{{.Title}} - - -

    Hi {{.Name}}

    - - diff --git a/_examples/view/template_html_1/main.go b/_examples/view/template_html_1/main.go deleted file mode 100644 index a2a3b45a3d..0000000000 --- a/_examples/view/template_html_1/main.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -type mypage struct { - Title string - Message string -} - -func main() { - app := iris.New() - - app.RegisterView(iris.HTML("./templates", ".html").Layout("layout.html")) - // TIP: append .Reload(true) to reload the templates on each request. - - app.Get("/", func(ctx iris.Context) { - ctx.CompressWriter(true) - ctx.ViewData("", mypage{"My Page title", "Hello world!"}) - ctx.View("mypage.html") - // Note that: you can pass "layout" : "otherLayout.html" to bypass the config's Layout property - // or view.NoLayout to disable layout on this render action. - // third is an optional parameter - }) - - // http://localhost:8080 - app.Listen(":8080") -} diff --git a/_examples/view/template_html_1/templates/layout.html b/_examples/view/template_html_1/templates/layout.html deleted file mode 100644 index 96f0c7532f..0000000000 --- a/_examples/view/template_html_1/templates/layout.html +++ /dev/null @@ -1,11 +0,0 @@ - - -My Layout - - - -

    [layout] Body content is below...

    - - {{ yield }} - - diff --git a/_examples/view/template_html_1/templates/mypage.html b/_examples/view/template_html_1/templates/mypage.html deleted file mode 100644 index 0f85c0ab7c..0000000000 --- a/_examples/view/template_html_1/templates/mypage.html +++ /dev/null @@ -1,4 +0,0 @@ -

    - Title: {{.Title}} -

    -

    Message: {{.Message}}

    \ No newline at end of file diff --git a/_examples/view/template_html_2/README.md b/_examples/view/template_html_2/README.md deleted file mode 100644 index f4f5ac99cc..0000000000 --- a/_examples/view/template_html_2/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## Info - -This folder examines the {{render "dir/templatefilename" .}} functionality to manually render any template inside any template diff --git a/_examples/view/template_html_2/main.go b/_examples/view/template_html_2/main.go deleted file mode 100644 index 270b99ba58..0000000000 --- a/_examples/view/template_html_2/main.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - tmpl := iris.HTML("./templates", ".html") - tmpl.Layout("layouts/layout.html") - tmpl.AddFunc("greet", func(s string) string { - return "Greetings " + s + "!" - }) - - app.RegisterView(tmpl) - - app.Get("/", func(ctx iris.Context) { - if err := ctx.View("page1.html"); err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.Writef(err.Error()) - } - }) - - // remove the layout for a specific route - app.Get("/nolayout", func(ctx iris.Context) { - ctx.ViewLayout(iris.NoLayout) - if err := ctx.View("page1.html"); err != nil { - ctx.StatusCode(iris.StatusInternalServerError) - ctx.Writef(err.Error()) - } - }) - - // set a layout for a party, .Layout should be BEFORE any Get or other Handle party's method - my := app.Party("/my").Layout("layouts/mylayout.html") - { // both of these will use the layouts/mylayout.html as their layout. - my.Get("/", func(ctx iris.Context) { - ctx.View("page1.html") - }) - my.Get("/other", func(ctx iris.Context) { - ctx.View("page1.html") - }) - } - - // http://localhost:8080 - // http://localhost:8080/nolayout - // http://localhost:8080/my - // http://localhost:8080/my/other - app.Listen(":8080") -} diff --git a/_examples/view/template_html_2/templates/layouts/layout.html b/_examples/view/template_html_2/templates/layouts/layout.html deleted file mode 100644 index eb543f0ed8..0000000000 --- a/_examples/view/template_html_2/templates/layouts/layout.html +++ /dev/null @@ -1,12 +0,0 @@ - - -Layout - - - -

    This is the global layout

    -
    - - {{ yield }} - - diff --git a/_examples/view/template_html_2/templates/layouts/mylayout.html b/_examples/view/template_html_2/templates/layouts/mylayout.html deleted file mode 100644 index d87575d3b9..0000000000 --- a/_examples/view/template_html_2/templates/layouts/mylayout.html +++ /dev/null @@ -1,12 +0,0 @@ - - -my Layout - - - -

    This is the layout for the /my/ and /my/other routes only

    -
    - - {{ yield }} - - diff --git a/_examples/view/template_html_2/templates/page1.html b/_examples/view/template_html_2/templates/page1.html deleted file mode 100644 index 8c1d8ed68c..0000000000 --- a/_examples/view/template_html_2/templates/page1.html +++ /dev/null @@ -1,7 +0,0 @@ -
    - -

    Page 1 {{ greet "iris developer"}}

    - - {{ render "partials/page1_partial1.html" }} - -
    diff --git a/_examples/view/template_html_2/templates/partials/page1_partial1.html b/_examples/view/template_html_2/templates/partials/page1_partial1.html deleted file mode 100644 index 8af006d242..0000000000 --- a/_examples/view/template_html_2/templates/partials/page1_partial1.html +++ /dev/null @@ -1,3 +0,0 @@ -
    -

    Page 1's Partial 1

    -
    diff --git a/_examples/view/template_html_3/main.go b/_examples/view/template_html_3/main.go deleted file mode 100644 index c9fcb38107..0000000000 --- a/_examples/view/template_html_3/main.go +++ /dev/null @@ -1,66 +0,0 @@ -// Package main an example on how to naming your routes & use the custom 'url path' HTML Template Engine, same for other template engines. -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - app.RegisterView(iris.HTML("./templates", ".html").Reload(true)) - - mypathRoute := app.Get("/mypath", writePathHandler) - mypathRoute.Name = "my-page1" - - mypath2Route := app.Get("/mypath2/{paramfirst}/{paramsecond}", writePathHandler) - mypath2Route.Name = "my-page2" - - mypath3Route := app.Get("/mypath3/{paramfirst}/statichere/{paramsecond}", writePathHandler) - mypath3Route.Name = "my-page3" - - mypath4Route := app.Get("/mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}", writePathHandler) - // same as: app.Get("/mypath4/:paramfirst/statichere/:paramsecond/:otherparam/*something", writePathHandler) - mypath4Route.Name = "my-page4" - - // same with Handle/Func - mypath5Route := app.Handle("GET", "/mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{something:path}", writePathHandler) - mypath5Route.Name = "my-page5" - - mypath6Route := app.Get("/mypath6/{paramfirst}/{paramsecond}/statichere/{paramThirdAfterStatic}", writePathHandler) - mypath6Route.Name = "my-page6" - - app.Get("/", func(ctx iris.Context) { - // for /mypath6... - paramsAsArray := []string{"theParam1", "theParam2", "paramThirdAfterStatic"} - ctx.ViewData("ParamsAsArray", paramsAsArray) - if err := ctx.View("page.html"); err != nil { - panic(err) - } - }) - - app.Get("/redirect/{namedRoute}", func(ctx iris.Context) { - routeName := ctx.Params().Get("namedRoute") - r := app.GetRoute(routeName) - if r == nil { - ctx.StatusCode(404) - ctx.Writef("Route with name %s not found", routeName) - return - } - - println("The path of " + routeName + "is: " + r.Path) - // if routeName == "my-page1" - // prints: The path of of my-page1 is: /mypath - // if it's a path which takes named parameters - // then use "r.ResolvePath(paramValuesHere)" - ctx.Redirect(r.Path) - // http://localhost:8080/redirect/my-page1 will redirect to -> http://localhost:8080/mypath - }) - - // http://localhost:8080 - // http://localhost:8080/redirect/my-page1 - app.Listen(":8080") -} - -func writePathHandler(ctx iris.Context) { - ctx.Writef("Hello from %s.", ctx.Path()) -} diff --git a/_examples/view/template_html_3/templates/page.html b/_examples/view/template_html_3/templates/page.html deleted file mode 100644 index 8835b3d055..0000000000 --- a/_examples/view/template_html_3/templates/page.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - template_html_3 - - - - - -
    /mypath -
    -
    - - /mypath2/{paramfirst}/{paramsecond} -
    -
    - - /mypath3/{paramfirst}/statichere/{paramsecond} -
    -
    - - - /mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path} -
    -
    - - - /mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{anything:path} -
    -
    - - - /mypath6/{paramfirst}/{paramsecond}/statichere/{paramThirdAfterStatic} - - - - \ No newline at end of file diff --git a/_examples/view/template_html_4/hosts b/_examples/view/template_html_4/hosts deleted file mode 100644 index 36bb689bc1..0000000000 --- a/_examples/view/template_html_4/hosts +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 1993-2009 Microsoft Corp. -# -# This is a sample HOSTS file used by Microsoft TCP/IP for Windows. -# -# This file contains the mappings of IP addresses to host names. Each -# entry should be kept on an individual line. The IP address should -# be placed in the first column followed by the corresponding host name. -# The IP address and the host name should be separated by at least one -# space. -# -# Additionally, comments (such as these) may be inserted on individual -# lines or following the machine name denoted by a '#' symbol. -# -# For example: -# -# 102.54.94.97 rhino.acme.com # source server -# 38.25.63.10 x.acme.com # x client host - -# localhost name resolution is handled within DNS itself. -127.0.0.1 localhost -::1 localhost -#-iris-For development machine, you have to configure your dns also for online, search google how to do it if you don't know - -127.0.0.1 username1.127.0.0.1 -127.0.0.1 username2.127.0.0.1 -127.0.0.1 username3.127.0.0.1 -127.0.0.1 username4.127.0.0.1 -127.0.0.1 username5.127.0.0.1 -# note that you can always use custom subdomains -#-END iris- - -# Windows: Drive:/Windows/system32/drivers/etc/hosts, on Linux: /etc/hosts \ No newline at end of file diff --git a/_examples/view/template_html_4/main.go b/_examples/view/template_html_4/main.go deleted file mode 100644 index ef0b97b808..0000000000 --- a/_examples/view/template_html_4/main.go +++ /dev/null @@ -1,76 +0,0 @@ -// Package main an example on how to naming your routes & use the custom 'url' HTML Template Engine, same for other template engines. -package main - -import ( - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/core/router" -) - -const ( - host = "127.0.0.1:8080" -) - -func main() { - app := iris.New() - - // create a custom path reverser, iris let you define your own host and scheme - // which is useful when you have nginx or caddy in front of iris. - rv := router.NewRoutePathReverser(app, router.WithHost(host), router.WithScheme("http")) - // locate and define our templates as usual. - templates := iris.HTML("./templates", ".html") - // add a custom func of "url" and pass the rv.URL as its template function body, - // so {{url "routename" "paramsOrSubdomainAsFirstArgument"}} will work inside our templates. - templates.AddFunc("url", rv.URL) - - app.RegisterView(templates) - - // wildcard subdomain, will catch username1.... username2.... username3... username4.... username5... - // that our below links are providing via page.html's first argument which is the subdomain. - - subdomain := app.Party("*.") - - mypathRoute := subdomain.Get("/mypath", emptyHandler) - mypathRoute.Name = "my-page1" - - mypath2Route := subdomain.Get("/mypath2/{paramfirst}/{paramsecond}", emptyHandler) - mypath2Route.Name = "my-page2" - - mypath3Route := subdomain.Get("/mypath3/{paramfirst}/statichere/{paramsecond}", emptyHandler) - mypath3Route.Name = "my-page3" - - mypath4Route := subdomain.Get("/mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}", emptyHandler) - mypath4Route.Name = "my-page4" - - mypath5Route := subdomain.Handle("GET", "/mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{something:path}", emptyHandler) - mypath5Route.Name = "my-page5" - - mypath6Route := subdomain.Get("/mypath6/{paramfirst}/{paramsecond}/staticParam/{paramThirdAfterStatic}", emptyHandler) - mypath6Route.Name = "my-page6" - - app.Get("/", func(ctx iris.Context) { - // for username5./mypath6... - paramsAsArray := []string{"username5", "theParam1", "theParam2", "paramThirdAfterStatic"} - ctx.ViewData("ParamsAsArray", paramsAsArray) - if err := ctx.View("page.html"); err != nil { - panic(err) - } - }) - - // simple path so you can test it without host mapping and subdomains, - // at view it make uses of {{urlpath ...}} - // in order to showcase you that you can use it - // if you don't want the entire scheme and host to be part of the url. - app.Get("/mypath7/{paramfirst}/{paramsecond}/static/{paramthird}", emptyHandler).Name = "my-page7" - - // http://127.0.0.1:8080 - app.Listen(host) -} - -func emptyHandler(ctx iris.Context) { - ctx.Writef("Hello from subdomain: %s , you're in path: %s", ctx.Subdomain(), ctx.Path()) -} - -// Note: -// If you got an empty string on {{ url }} or {{ urlpath }} it means that -// args length are not aligned with the route's parameters length -// or the route didn't found by the passed name. diff --git a/_examples/view/template_html_4/templates/page.html b/_examples/view/template_html_4/templates/page.html deleted file mode 100644 index 9e35dc377e..0000000000 --- a/_examples/view/template_html_4/templates/page.html +++ /dev/null @@ -1,43 +0,0 @@ - - -username1.127.0.0.1:8080/mypath -
    -
    - - - username2.127.0.0.1:8080/mypath2/{paramfirst}/{paramsecond} - -
    -
    - - - username3.127.0.0.1:8080/mypath3/{paramfirst}/statichere/{paramsecond} - -
    -
    - - - username4.127.0.0.1:8080/mypath4/{paramfirst}/statichere/{paramsecond}/{otherParam}/{something:path} - -
    -
    - - - username5.127.0.0.1:8080/mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{something:path} - -
    -
    - - - username5.127.0.0.1:8080/mypath6/{paramfirst}/{paramsecond}/staticParam/{paramThirdAfterStatic} - -
    -
    - - - mypath7/{paramfirst}/{paramsecond}/static/{paramthird} - -
    -
    - diff --git a/_examples/view/template_html_5/main.go b/_examples/view/template_html_5/main.go deleted file mode 100644 index 7e79e0b236..0000000000 --- a/_examples/view/template_html_5/main.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - app.RegisterView(iris.HTML("./views", ".html").Layout("layout.html")) - // TIP: append .Reload(true) to reload the templates on each request. - - app.Get("/home", func(ctx iris.Context) { - ctx.ViewData("title", "Home page") - ctx.View("home.html") - // Note that: you can pass "layout" : "otherLayout.html" to bypass the config's Layout property - // or view.NoLayout to disable layout on this render action. - // third is an optional parameter - }) - - app.Get("/about", func(ctx iris.Context) { - ctx.View("about.html") - }) - - app.Get("/user/index", func(ctx iris.Context) { - ctx.View("user/index.html") - }) - - // http://localhost:8080 - app.Listen(":8080") -} diff --git a/_examples/view/template_html_5/views/about.html b/_examples/view/template_html_5/views/about.html deleted file mode 100644 index a264299265..0000000000 --- a/_examples/view/template_html_5/views/about.html +++ /dev/null @@ -1,15 +0,0 @@ -{{ define "about-head"}} - about page - -{{ end }} - -{{ define "about-body"}} - extend body content in layout. -{{ end }} -
    - Hello about page -
    \ No newline at end of file diff --git a/_examples/view/template_html_5/views/home.html b/_examples/view/template_html_5/views/home.html deleted file mode 100644 index 365990090f..0000000000 --- a/_examples/view/template_html_5/views/home.html +++ /dev/null @@ -1,11 +0,0 @@ -{{ define "home-head"}} - {{.title}} - -{{ end }} -
    - Hello home page -
    \ No newline at end of file diff --git a/_examples/view/template_html_5/views/layout.html b/_examples/view/template_html_5/views/layout.html deleted file mode 100644 index c374205f7e..0000000000 --- a/_examples/view/template_html_5/views/layout.html +++ /dev/null @@ -1,11 +0,0 @@ - - -{{ part "head" }} - - -

    [layout] Body content is below...

    - {{ part "body" }} - - {{ yield }} - - diff --git a/_examples/view/template_html_5/views/user/index.html b/_examples/view/template_html_5/views/user/index.html deleted file mode 100644 index 6c73fb2e4a..0000000000 --- a/_examples/view/template_html_5/views/user/index.html +++ /dev/null @@ -1,10 +0,0 @@ -{{ define "user/index-head"}} - -{{ end }} -
    - Hello user index page -
    \ No newline at end of file diff --git a/_examples/view/template_jet_0/README.md b/_examples/view/template_jet_0/README.md deleted file mode 100644 index 627e5b3e46..0000000000 --- a/_examples/view/template_jet_0/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Jet Engine Example - -This example is a fork of to work with Iris, so you can notice the differences side by side. - -Read more at: https://github.com/CloudyKit/jet/blob/master/docs/syntax.md - -> The Iris Jet View Engine fixes some bugs that the underline [jet template parser](https://github.com/CloudyKit/jet) currently has. - - -Continue by learning how you can [serve embedded templates](../template_jet_1_embedded). \ No newline at end of file diff --git a/_examples/view/template_jet_0/main.go b/_examples/view/template_jet_0/main.go deleted file mode 100644 index a7cb43c819..0000000000 --- a/_examples/view/template_jet_0/main.go +++ /dev/null @@ -1,146 +0,0 @@ -// Package main shows how to use jet template parser with ease using the Iris built-in Jet view engine. -// This example is customized fork of https://github.com/CloudyKit/jet/tree/master/examples/todos, so you can -// notice the differences side by side. -package main - -import ( - "bytes" - "encoding/base64" - "fmt" - "os" - "reflect" - "strings" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/view" -) - -type tTODO struct { - Text string - Done bool -} - -type doneTODOs struct { - list map[string]*tTODO - keys []string - len int - i int -} - -func (dt *doneTODOs) New(todos map[string]*tTODO) *doneTODOs { - dt.len = len(todos) - for k := range todos { - dt.keys = append(dt.keys, k) - } - dt.list = todos - return dt -} - -// Range satisfies the jet.Ranger interface and only returns TODOs that are done, -// even when the list contains TODOs that are not done. -func (dt *doneTODOs) Range() (reflect.Value, reflect.Value, bool) { - for dt.i < dt.len { - key := dt.keys[dt.i] - dt.i++ - if dt.list[key].Done { - return reflect.ValueOf(key), reflect.ValueOf(dt.list[key]), false - } - } - return reflect.Value{}, reflect.Value{}, true -} - -// Note: jet version 4 requires this. -func (dt *doneTODOs) ProvidesIndex() bool { return true } - -func (dt *doneTODOs) Render(r *view.JetRuntime) { - r.Write([]byte(fmt.Sprintf("custom renderer"))) -} - -// Render implements jet.Renderer interface -func (t *tTODO) Render(r *view.JetRuntime) { - done := "yes" - if !t.Done { - done = "no" - } - r.Write([]byte(fmt.Sprintf("TODO: %s (done: %s)", t.Text, done))) -} - -func main() { - // - // Type aliases: - // view.JetRuntimeVars = jet.VarMap - // view.JetRuntime = jet.Runtime - // view.JetArguments = jet.Arguments - // - // Iris also gives you the ability to put runtime variables - // from middlewares as well, by: - // view.AddJetRuntimeVars(ctx, vars) - // or tmpl.AddRuntimeVars(ctx, vars) - app := iris.New() - tmpl := iris.Jet("./views", ".jet") // <-- - tmpl.Reload(true) // remove in production. - tmpl.AddFunc("base64", func(a view.JetArguments) reflect.Value { - a.RequireNumOfArguments("base64", 1, 1) - - buffer := bytes.NewBuffer(nil) - fmt.Fprint(buffer, a.Get(0)) - - return reflect.ValueOf(base64.URLEncoding.EncodeToString(buffer.Bytes())) - }) - app.RegisterView(tmpl) // <-- - - todos := map[string]*tTODO{ - "example-todo-1": {Text: "Add an show todo page to the example project", Done: true}, - "example-todo-2": {Text: "Add an add todo page to the example project"}, - "example-todo-3": {Text: "Add an update todo page to the example project"}, - "example-todo-4": {Text: "Add an delete todo page to the example project", Done: true}, - } - - app.Get("/", func(ctx iris.Context) { - err := ctx.View("todos/index.jet", todos) // <-- - // Note that the `ctx.View` already logs the error if logger level is allowing it and returns the error. - if err != nil { - ctx.StopWithText(iris.StatusInternalServerError, "Templates not rendered!") - } - }) - - app.Get("/todo", func(ctx iris.Context) { - id := ctx.URLParam("id") - todo, ok := todos[id] - if !ok { - ctx.Redirect("/") - return - } - - ctx.View("todos/show.jet", todo) - }) - app.Get("/all-done", func(ctx iris.Context) { - // vars := make(view.JetRuntimeVars) - // vars.Set("showingAllDone", true) - // vars.Set("title", "Todos - All Done") - // view.AddJetRuntimeVars(ctx, vars) - // ctx.View("todos/index.jet", (&doneTODOs{}).New(todos)) - // - // OR - - ctx.ViewData("showingAllDone", true) - ctx.ViewData("title", "Todos - All Done") - - // Use ctx.ViewData("_jet", jetData) - // if using as middleware and you want - // to pre-set the value or even change it later on from another next middleware. - // ctx.ViewData("_jet", (&doneTODOs{}).New(todos)) - // and ctx.View("todos/index.jet") - // OR - ctx.View("todos/index.jet", (&doneTODOs{}).New(todos)) - }) - - port := os.Getenv("PORT") - if len(port) == 0 { - port = ":8080" - } else if !strings.HasPrefix(":", port) { - port = ":" + port - } - - app.Listen(port) -} diff --git a/_examples/view/template_jet_0/views/layouts/application.jet b/_examples/view/template_jet_0/views/layouts/application.jet deleted file mode 100644 index 9fce2365a3..0000000000 --- a/_examples/view/template_jet_0/views/layouts/application.jet +++ /dev/null @@ -1,10 +0,0 @@ - - - - - {{ isset(title) ? title : "" }} - - - {{block documentBody()}}{{end}} - - diff --git a/_examples/view/template_jet_0/views/todos/index.jet b/_examples/view/template_jet_0/views/todos/index.jet deleted file mode 100644 index 5b700e2823..0000000000 --- a/_examples/view/template_jet_0/views/todos/index.jet +++ /dev/null @@ -1,30 +0,0 @@ -{{extends "../layouts/application.jet"}} - -{{block button(label, href="javascript:void(0)")}} - {{ label }} -{{end}} - -{{block ul()}} -
      - {{yield content}} -
    -{{end}} - -{{block documentBody()}} -

    List of TODOs

    - - {{if isset(showingAllDone) && showingAllDone}} -

    Showing only TODOs that are done

    - {{else}} -

    Show only TODOs that are done

    - {{end}} - - {{yield ul() content}} - {{range id, value := .}} -
  • - {{ value.Text }} - {{yield button(label="UP", href="/update/?id="+base64(id))}} - {{yield button(href="/delete/?id="+id, label="DL")}} -
  • - {{end}} - {{end}} -{{end}} diff --git a/_examples/view/template_jet_0/views/todos/show.jet b/_examples/view/template_jet_0/views/todos/show.jet deleted file mode 100644 index cc37722e51..0000000000 --- a/_examples/view/template_jet_0/views/todos/show.jet +++ /dev/null @@ -1,9 +0,0 @@ -{{extends "../layouts/application.jet"}} - -{{block documentBody()}} -

    Show TODO

    -

    This uses a custom renderer by implementing the Renderer interface. -

    - {{.}} -

    -{{end}} diff --git a/_examples/view/template_jet_1_embedded/README.md b/_examples/view/template_jet_1_embedded/README.md deleted file mode 100644 index bb78ada652..0000000000 --- a/_examples/view/template_jet_1_embedded/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Jet Engine Example (Embedded) - -Take a look at the [../template_jet_0](../template_jet_0)'s README first. - -This example teaches you how to use jet templates embedded in your applications with ease using the Iris built-in Jet view engine. - -This example is a customized fork of https://github.com/CloudyKit/jet/tree/master/examples/asset_packaging, so you can -notice the differences side by side. For example, you don't have to use any external package inside your application, -Iris manually builds the template loader for binary data when Asset and AssetNames are available through tools like the [go-bindata](github.com/go-bindata/go-bindata). - -Note that you can still use any custom loaders through the `JetEngine.SetLoader` -which overrides any previous loaders like `JetEngine.Binary` we use on this example. - -## How to run - -```sh -$ go get -u github.com/go-bindata/go-bindata/v3/go-bindata -$ go-bindata ./views/... -$ go build -$ ./template_jet_0_embedded -``` - -Repeat the above steps on any `./views` changes. - -> html files are not used, only binary data. You can move or delete the `./views` folder. diff --git a/_examples/view/template_jet_1_embedded/bindata.go b/_examples/view/template_jet_1_embedded/bindata.go deleted file mode 100644 index 11989e7b0f..0000000000 --- a/_examples/view/template_jet_1_embedded/bindata.go +++ /dev/null @@ -1,308 +0,0 @@ -// Code generated by go-bindata. DO NOT EDIT. -// sources: -// views/includes/_partial.jet -// views/includes/blocks.jet -// views/index.jet -// views/layouts/application.jet -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data []byte, name string) ([]byte, error) { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -func (fi bindataFileInfo) Name() string { - return fi.name -} -func (fi bindataFileInfo) Size() int64 { - return fi.size -} -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} -func (fi bindataFileInfo) IsDir() bool { - return false -} -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -var _viewsIncludes_partialJet = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb2\x29\xb0\xf3\xcc\x4b\xce\x29\x4d\x49\x4d\x51\x28\x48\x2c\x2a\xc9\x4c\xcc\xb1\xd1\x2f\xb0\xe3\xe5\x02\x04\x00\x00\xff\xff\x90\x62\x4f\xfb\x19\x00\x00\x00") - -func viewsIncludes_partialJetBytes() ([]byte, error) { - return bindataRead( - _viewsIncludes_partialJet, - "views/includes/_partial.jet", - ) -} - -func viewsIncludes_partialJet() (*asset, error) { - bytes, err := viewsIncludes_partialJetBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "views/includes/_partial.jet", size: 25, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _viewsIncludesBlocksJet = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xaa\xae\x4e\xca\xc9\x4f\xce\x56\xc8\x4d\xcd\x2b\xd5\xd0\xac\xad\xe5\xe5\x52\x50\xb0\x29\xb0\x0b\xc9\x48\x05\x0b\x29\x40\x64\xcb\x13\x8b\x15\x32\xf3\xca\xf2\xb3\x53\x53\xf4\x6c\xf4\x0b\xec\x78\xb9\xaa\xab\x53\xf3\x52\x40\xca\x01\x01\x00\x00\xff\xff\xa0\xd9\xd9\x5d\x41\x00\x00\x00") - -func viewsIncludesBlocksJetBytes() ([]byte, error) { - return bindataRead( - _viewsIncludesBlocksJet, - "views/includes/blocks.jet", - ) -} - -func viewsIncludesBlocksJet() (*asset, error) { - bytes, err := viewsIncludesBlocksJetBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "views/includes/blocks.jet", size: 65, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _viewsIndexJet = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x64\x90\xb1\x6a\xf3\x30\x14\x85\xf7\x1f\xfe\x77\x38\xf5\xd2\x74\xa9\xc9\x5a\x8c\x87\x42\x86\x2e\x5d\xfa\x00\x45\xb6\x6e\xb0\x1a\x59\x57\xf8\x5e\xd7\x36\x22\xef\x5e\x2c\x9b\x10\xda\xed\x48\xe7\x3b\xe8\x43\x29\xd1\xac\x14\xac\xa0\xf0\x66\xe1\x51\xa5\x34\x31\x7a\xd7\x1a\x75\x1c\x9e\xbf\x48\x8b\xeb\xf5\xff\xbf\x94\x5c\x1f\x79\x50\x14\x2e\xb4\x7e\xb4\x24\x65\xe3\xb9\xbd\xc8\x8d\x58\x99\x7c\x05\xcb\xed\xd8\x53\xd0\x57\xb6\xcb\xe1\x69\xed\x80\xaa\x3b\xd6\xa7\xbe\x21\x6b\xc9\x82\x66\xd3\x47\x4f\x55\xd9\x1d\xeb\x75\x08\x54\xc1\x7c\xd7\x6b\x00\x52\x5a\x1c\x79\x8b\x9e\xc2\x78\x5b\x97\x5b\xbf\xe6\x94\x76\x83\x3b\x95\xcf\x68\x06\x75\xc6\xdf\xc9\x64\xf0\x8c\x87\x9d\x79\x3b\x9f\x66\x27\x2a\x87\xc2\x32\xc9\x3b\x6b\x3e\x66\x7e\x7f\x03\xa8\x62\xfd\xd1\xf1\x24\xe8\x78\xfa\x33\xc4\xc4\xc3\x45\x5e\xf0\x7b\x8e\xc9\x08\x02\x2b\x76\xde\xa2\xa1\xd6\x8c\x42\x70\x9a\xe1\xf0\xa8\xa0\x4c\x57\x65\xac\x37\x31\x0a\x76\xfb\xd4\x3d\xfc\x04\x00\x00\xff\xff\x28\x5a\x9d\x42\x85\x01\x00\x00") - -func viewsIndexJetBytes() ([]byte, error) { - return bindataRead( - _viewsIndexJet, - "views/index.jet", - ) -} - -func viewsIndexJet() (*asset, error) { - bytes, err := viewsIndexJetBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "views/index.jet", size: 389, mode: os.FileMode(438), modTime: time.Unix(1594059793, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _viewsLayoutsApplicationJet = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x3c\x8e\xbd\xae\xc2\x30\x0c\x85\xf7\x4a\x7d\x07\xdf\x4c\x97\x01\x75\x65\x70\x3b\x00\x65\x85\xa1\x0c\x8c\x69\x6d\x11\x44\x7e\x10\x18\x89\x2a\xca\xbb\x23\xda\xc0\x64\xcb\x3e\xdf\xa7\x83\x7f\xdb\xfd\xa6\x3b\x1d\x5a\x30\xe2\x6c\x53\x16\xf8\x99\x60\xb5\x3f\xd7\x8a\xbd\x6a\xca\x02\x00\x0d\x6b\x9a\x36\x00\x74\x2c\x1a\x06\xa3\xef\x0f\x96\x5a\x1d\xbb\xdd\x72\xa5\xbe\x3f\xb9\x88\xe5\xa6\x75\x3d\x13\x31\x01\xbf\xb4\xbb\x59\xc6\x6a\xbe\x4f\xaa\xea\xe7\xc2\x3e\xd0\x98\xc9\x18\x7b\x1b\x86\x2b\x50\x18\x9e\x8e\xbd\xac\x03\x8d\xff\x8b\x94\x62\x64\x4f\x29\xcd\x64\xce\x63\x95\xab\xbe\x03\x00\x00\xff\xff\xee\xc2\x94\xa4\xbc\x00\x00\x00") - -func viewsLayoutsApplicationJetBytes() ([]byte, error) { - return bindataRead( - _viewsLayoutsApplicationJet, - "views/layouts/application.jet", - ) -} - -func viewsLayoutsApplicationJet() (*asset, error) { - bytes, err := viewsLayoutsApplicationJetBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "views/layouts/application.jet", size: 188, mode: os.FileMode(438), modTime: time.Unix(1565946441, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "views/includes/_partial.jet": viewsIncludes_partialJet, - "views/includes/blocks.jet": viewsIncludesBlocksJet, - "views/index.jet": viewsIndexJet, - "views/layouts/application.jet": viewsLayoutsApplicationJet, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"} -// AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("notexist") would return an error -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "views": {nil, map[string]*bintree{ - "includes": {nil, map[string]*bintree{ - "_partial.jet": {viewsIncludes_partialJet, map[string]*bintree{}}, - "blocks.jet": {viewsIncludesBlocksJet, map[string]*bintree{}}, - }}, - "index.jet": {viewsIndexJet, map[string]*bintree{}}, - "layouts": {nil, map[string]*bintree{ - "application.jet": {viewsLayoutsApplicationJet, map[string]*bintree{}}, - }}, - }}, -}} - -// RestoreAsset restores an asset under the given directory -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil -} - -// RestoreAssets restores an asset under the given directory recursively -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) -} diff --git a/_examples/view/template_jet_1_embedded/main.go b/_examples/view/template_jet_1_embedded/main.go deleted file mode 100644 index 028fb7be3c..0000000000 --- a/_examples/view/template_jet_1_embedded/main.go +++ /dev/null @@ -1,34 +0,0 @@ -// Package main shows how to use jet templates embedded in your application with ease using the Iris built-in Jet view engine. -// This example is a customized fork of https://github.com/CloudyKit/jet/tree/master/examples/asset_packaging, so you can -// notice the differences side by side. For example, you don't have to use any external package inside your application, -// Iris manually builds the template loader for binary data when Asset and AssetNames are available via tools like the go-bindata. -package main - -import ( - "os" - "strings" - - "github.com/kataras/iris/v12" -) - -// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata -// $ go-bindata ./views/... -// $ go run . -func main() { - app := iris.New() - tmpl := iris.Jet("./views", ".jet").Binary(Asset, AssetNames) - app.RegisterView(tmpl) - - app.Get("/", func(ctx iris.Context) { - ctx.View("index.jet") - }) - - port := os.Getenv("PORT") - if len(port) == 0 { - port = ":8080" - } else if !strings.HasPrefix(":", port) { - port = ":" + port - } - - app.Listen(port) -} diff --git a/_examples/view/template_jet_1_embedded/views/includes/_partial.jet b/_examples/view/template_jet_1_embedded/views/includes/_partial.jet deleted file mode 100644 index 3ebaa7af7b..0000000000 --- a/_examples/view/template_jet_1_embedded/views/includes/_partial.jet +++ /dev/null @@ -1 +0,0 @@ -

    Included partial

    diff --git a/_examples/view/template_jet_1_embedded/views/includes/blocks.jet b/_examples/view/template_jet_1_embedded/views/includes/blocks.jet deleted file mode 100644 index 14c074eae1..0000000000 --- a/_examples/view/template_jet_1_embedded/views/includes/blocks.jet +++ /dev/null @@ -1,3 +0,0 @@ -{{block menu()}} -

    The menu block was invoked.

    -{{end}} diff --git a/_examples/view/template_jet_1_embedded/views/index.jet b/_examples/view/template_jet_1_embedded/views/index.jet deleted file mode 100644 index 670f11a7d4..0000000000 --- a/_examples/view/template_jet_1_embedded/views/index.jet +++ /dev/null @@ -1,16 +0,0 @@ -{{extends "layouts/application.jet"}} -{{import "includes/blocks.jet"}} - -{{block documentBody()}} -

    Embedded example

    - - - - {{include "includes/_partial.jet"}} - - {{if !includeIfExists("doesNotExist.jet")}} -

    Shows how !includeIfExists works: doesNotExist.jet was not included because it doesn't exist.

    - {{end}} -{{end}} diff --git a/_examples/view/template_jet_1_embedded/views/layouts/application.jet b/_examples/view/template_jet_1_embedded/views/layouts/application.jet deleted file mode 100644 index 16117b7127..0000000000 --- a/_examples/view/template_jet_1_embedded/views/layouts/application.jet +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Embedded example - - - {{block documentBody()}}{{end}} - - diff --git a/_examples/view/template_jet_2/main.go b/_examples/view/template_jet_2/main.go deleted file mode 100644 index db872d2be5..0000000000 --- a/_examples/view/template_jet_2/main.go +++ /dev/null @@ -1,70 +0,0 @@ -// Package main an example on how to naming your routes & use the custom 'url path' Jet Template Engine. -package main - -import ( - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - app.RegisterView(iris.Jet("./views", ".jet").Reload(true)) - - mypathRoute := app.Get("/mypath", writePathHandler) - mypathRoute.Name = "my-page1" - - mypath2Route := app.Get("/mypath2/{paramfirst}/{paramsecond}", writePathHandler) - mypath2Route.Name = "my-page2" - - mypath3Route := app.Get("/mypath3/{paramfirst}/statichere/{paramsecond}", writePathHandler) - mypath3Route.Name = "my-page3" - - mypath4Route := app.Get("/mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}", writePathHandler) - // same as: app.Get("/mypath4/:paramfirst/statichere/:paramsecond/:otherparam/*something", writePathHandler) - mypath4Route.Name = "my-page4" - - // same with Handle/Func - mypath5Route := app.Handle("GET", "/mypath5/{paramfirst:int}/statichere/{paramsecond}/{otherparam}/anything/{something:path}", writePathHandlerPage5) - mypath5Route.Name = "my-page5" - - mypath6Route := app.Get("/mypath6/{paramfirst}/{paramsecond}/statichere/{paramThirdAfterStatic}", writePathHandler) - mypath6Route.Name = "my-page6" - - app.Get("/", func(ctx iris.Context) { - // for /mypath6... - paramsAsArray := []string{"theParam1", "theParam2", "paramThirdAfterStatic"} - ctx.ViewData("ParamsAsArray", paramsAsArray) - if err := ctx.View("page.jet"); err != nil { - panic(err) - } - }) - - app.Get("/redirect/{namedRoute}", func(ctx iris.Context) { - routeName := ctx.Params().Get("namedRoute") - r := app.GetRoute(routeName) - if r == nil { - ctx.StatusCode(iris.StatusNotFound) - ctx.Writef("Route with name %s not found", routeName) - return - } - - println("The path of " + routeName + "is: " + r.Path) - // if routeName == "my-page1" - // prints: The path of of my-page1 is: /mypath - // if it's a path which takes named parameters - // then use "r.ResolvePath(paramValuesHere)" - ctx.Redirect(r.Path) - // http://localhost:8080/redirect/my-page1 will redirect to -> http://localhost:8080/mypath - }) - - // http://localhost:8080 - // http://localhost:8080/redirect/my-page1 - app.Listen(":8080") -} - -func writePathHandler(ctx iris.Context) { - ctx.Writef("Hello from %s.", ctx.Path()) -} - -func writePathHandlerPage5(ctx iris.Context) { - ctx.Writef("Hello from %s.\nparamfirst(int)=%d", ctx.Path(), ctx.Params().GetIntDefault("paramfirst", 0)) -} diff --git a/_examples/view/template_jet_2/views/page.jet b/_examples/view/template_jet_2/views/page.jet deleted file mode 100644 index c7268d9d63..0000000000 --- a/_examples/view/template_jet_2/views/page.jet +++ /dev/null @@ -1,24 +0,0 @@ -/mypath -
    -
    -/mypath2/{paramfirst}/{paramsecond} -
    -
    - -/mypath3/{paramfirst}/statichere/{paramsecond} -
    -
    - - - /mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path} -
    -
    - - - /mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{anything:path} -
    -
    - - - /mypath6/{paramfirst}/{paramsecond}/statichere/{paramThirdAfterStatic} - diff --git a/_examples/view/template_jet_3/main.go b/_examples/view/template_jet_3/main.go deleted file mode 100644 index 6928243eae..0000000000 --- a/_examples/view/template_jet_3/main.go +++ /dev/null @@ -1,55 +0,0 @@ -package main - -import ( - "fmt" - "reflect" - "strings" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/view" -) - -// https://github.com/kataras/iris/issues/1443 - -func main() { - - tmpl := iris.Jet("./views", ".jet") - tmpl.Reload(true) - - val := reflect.ValueOf(ViewBuiler{}) - fns := val.Type() - for i := 0; i < fns.NumMethod(); i++ { - method := fns.Method(i) - tmpl.AddFunc(strings.ToLower(method.Name), val.Method(i).Interface()) - } - - app := iris.New() - app.RegisterView(tmpl) - - app.Get("/", func(ctx iris.Context) { - ctx.View("index.jet") - }) - - app.Listen(":8080") -} - -type ViewBuiler struct { -} - -func (ViewBuiler) Asset(a view.JetArguments) reflect.Value { - path := a.Get(0).String() - // fmt.Println(os.Getenv("APP_URL")) - return reflect.ValueOf(path) -} - -func (ViewBuiler) Style(a view.JetArguments) reflect.Value { - path := a.Get(0).String() - s := fmt.Sprintf(` `, path) - return reflect.ValueOf(s) -} - -func (ViewBuiler) Script(a view.JetArguments) reflect.Value { - path := a.Get(0).String() - s := fmt.Sprintf(``, path) - return reflect.ValueOf(s) -} diff --git a/_examples/view/template_jet_3/views/index.jet b/_examples/view/template_jet_3/views/index.jet deleted file mode 100644 index bfb1e3fa32..0000000000 --- a/_examples/view/template_jet_3/views/index.jet +++ /dev/null @@ -1,5 +0,0 @@ -{{ asset("./myasset.mp3")}} -
    -{{ style("my-stle.css")}} -
    -{{ script("my-script.js")}} \ No newline at end of file diff --git a/_examples/view/template_pug_0/main.go b/_examples/view/template_pug_0/main.go deleted file mode 100644 index a7d381866a..0000000000 --- a/_examples/view/template_pug_0/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -func main() { - app := iris.New() - - tmpl := iris.Pug("./templates", ".pug") - tmpl.Reload(true) // reload templates on each request (development mode) - tmpl.AddFunc("greet", func(s string) string { // add your template func here. - return "Greetings " + s + "!" - }) - - app.RegisterView(tmpl) - - app.Get("/", index) - - // http://localhost:8080 - app.Listen(":8080") -} - -func index(ctx iris.Context) { - ctx.ViewData("pageTitle", "My Index Page") - ctx.ViewData("youAreUsingJade", true) - // Q: why need extension .pug? - // A: Because you can register more than one view engine per Iris application. - ctx.View("index.pug") -} diff --git a/_examples/view/template_pug_0/templates/index.pug b/_examples/view/template_pug_0/templates/index.pug deleted file mode 100644 index e0f392389a..0000000000 --- a/_examples/view/template_pug_0/templates/index.pug +++ /dev/null @@ -1,25 +0,0 @@ -mixin withGo - | Generating Go html/template output. - -doctype html -html(lang="en") - head - title= .pageTitle - script(type='text/javascript'). - if (foo) { - bar(1 + 5) - } - body - h1 Jade - template engine - #container.col - if .youAreUsingJade - p {{ greet "iris user" }} - p You are amazing! - else - p Get on it! - p. - Jade is #[a(terse)] and simple - templating language with a - #[strong focus] on performance - and powerful features. - + withGo \ No newline at end of file diff --git a/_examples/view/template_pug_1/main.go b/_examples/view/template_pug_1/main.go deleted file mode 100644 index 27c50f06e9..0000000000 --- a/_examples/view/template_pug_1/main.go +++ /dev/null @@ -1,42 +0,0 @@ -// Package main shows an example of pug actions based on https://github.com/Joker/jade/tree/master/example/actions -package main - -import "github.com/kataras/iris/v12" - -type Person struct { - Name string - Age int - Emails []string - Jobs []*Job -} - -type Job struct { - Employer string - Role string -} - -func main() { - app := iris.New() - - tmpl := iris.Pug("./templates", ".pug") - app.RegisterView(tmpl) - - app.Get("/", index) - - // http://localhost:8080 - app.Listen(":8080") -} - -func index(ctx iris.Context) { - job1 := Job{Employer: "Monash B", Role: "Honorary"} - job2 := Job{Employer: "Box Hill", Role: "Head of HE"} - - person := Person{ - Name: "jan", - Age: 50, - Emails: []string{"jan@newmarch.name", "jan.newmarch@gmail.com"}, - Jobs: []*Job{&job1, &job2}, - } - - ctx.View("index.pug", person) -} diff --git a/_examples/view/template_pug_1/templates/index.pug b/_examples/view/template_pug_1/templates/index.pug deleted file mode 100644 index c6b21de2ef..0000000000 --- a/_examples/view/template_pug_1/templates/index.pug +++ /dev/null @@ -1,20 +0,0 @@ -doctype html -html(lang="en") - head - meta(charset="utf-8") - title Title - body - p ads - ul - li The name is {{.Name}}. - li The age is {{.Age}}. - - each _,_ in .Emails - div An email is {{.}} - - | {{ with .Jobs }} - each _,_ in . - div - An employer is {{.Employer}} - and the role is {{.Role}} - | {{ end }} \ No newline at end of file diff --git a/_examples/view/template_pug_2/main.go b/_examples/view/template_pug_2/main.go deleted file mode 100644 index 28186b14c7..0000000000 --- a/_examples/view/template_pug_2/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "html/template" - - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - - tmpl := iris.Pug("./templates", ".pug") - tmpl.Reload(true) // reload templates on each request (development mode) - tmpl.AddFunc("bold", func(s string) (template.HTML, error) { // add your template func here. - return template.HTML("" + s + ""), nil - }) - - app.RegisterView(tmpl) - - app.Get("/", index) - - // http://localhost:8080 - app.Listen(":8080") -} - -func index(ctx iris.Context) { - ctx.View("index.pug") -} diff --git a/_examples/view/template_pug_2/templates/footer.pug b/_examples/view/template_pug_2/templates/footer.pug deleted file mode 100644 index b7f5889366..0000000000 --- a/_examples/view/template_pug_2/templates/footer.pug +++ /dev/null @@ -1,2 +0,0 @@ -#footer - p Copyright (c) foobar \ No newline at end of file diff --git a/_examples/view/template_pug_2/templates/header.pug b/_examples/view/template_pug_2/templates/header.pug deleted file mode 100644 index a3f081eada..0000000000 --- a/_examples/view/template_pug_2/templates/header.pug +++ /dev/null @@ -1,4 +0,0 @@ -head - title My Site - \ No newline at end of file diff --git a/_examples/view/template_pug_2/templates/index.pug b/_examples/view/template_pug_2/templates/index.pug deleted file mode 100644 index ee11d9b4d1..0000000000 --- a/_examples/view/template_pug_2/templates/index.pug +++ /dev/null @@ -1,7 +0,0 @@ -doctype html -html - include header.pug - body - h1 My Site - p {{ bold "Welcome to my super lame site."}} - include footer.pug \ No newline at end of file diff --git a/_examples/view/template_pug_3/bindata.go b/_examples/view/template_pug_3/bindata.go deleted file mode 100644 index fb9d699afa..0000000000 --- a/_examples/view/template_pug_3/bindata.go +++ /dev/null @@ -1,269 +0,0 @@ -// Code generated for package main by go-bindata DO NOT EDIT. (@generated) -// sources: -// templates/index.pug -// templates/layout.pug -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data []byte, name string) ([]byte, error) { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -// Name return file name -func (fi bindataFileInfo) Name() string { - return fi.name -} - -// Size return file size -func (fi bindataFileInfo) Size() int64 { - return fi.size -} - -// Mode return file mode -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} - -// Mode return file modify time -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} - -// IsDir return file whether a directory -func (fi bindataFileInfo) IsDir() bool { - return fi.mode&os.ModeDir != 0 -} - -// Sys return file is sys mode -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -var _templatesIndexPug = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xad\x28\x49\xcd\x4b\x29\x56\xc8\x49\xac\xcc\x2f\x2d\xd1\x2b\x28\x4d\xe7\xe5\xe2\xe5\x4a\xca\xc9\x4f\xce\x56\x28\xc9\x2c\xc9\x49\xe5\xe5\x52\x80\x30\x14\x1c\x8b\x4a\x32\x93\x73\x52\x15\x42\x20\xc2\x30\x55\xc9\xf9\x79\x25\xa9\x79\x25\x20\x75\x19\x86\x0a\xbe\x95\x30\x75\x80\x00\x00\x00\xff\xff\xa6\xfd\x18\x8c\x5a\x00\x00\x00") - -func templatesIndexPugBytes() ([]byte, error) { - return bindataRead( - _templatesIndexPug, - "templates/index.pug", - ) -} - -func templatesIndexPug() (*asset, error) { - bytes, err := templatesIndexPugBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "templates/index.pug", size: 90, mode: os.FileMode(438), modTime: time.Unix(1581790962, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _templatesLayoutPug = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4a\xc9\x4f\x2e\xa9\x2c\x48\x55\xc8\x28\xc9\xcd\xe1\xe5\x82\x90\x0a\x0a\x19\xa9\x89\x29\x20\x5a\x41\x21\x29\x27\x3f\x39\x5b\xa1\x24\xb3\x24\x27\x15\x22\xa0\x00\xe1\x28\xb8\xa4\xa6\x25\x96\xe6\x94\x20\xa4\x92\xf2\x53\x2a\x91\xf5\x24\xe7\xe7\x95\xa4\xe6\x95\x00\x02\x00\x00\xff\xff\x5f\xa5\x93\xf9\x61\x00\x00\x00") - -func templatesLayoutPugBytes() ([]byte, error) { - return bindataRead( - _templatesLayoutPug, - "templates/layout.pug", - ) -} - -func templatesLayoutPug() (*asset, error) { - bytes, err := templatesLayoutPugBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "templates/layout.pug", size: 97, mode: os.FileMode(438), modTime: time.Unix(1581790962, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "templates/index.pug": templatesIndexPug, - "templates/layout.pug": templatesLayoutPug, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"} -// AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("notexist") would return an error -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "templates": {nil, map[string]*bintree{ - "index.pug": {templatesIndexPug, map[string]*bintree{}}, - "layout.pug": {templatesLayoutPug, map[string]*bintree{}}, - }}, -}} - -// RestoreAsset restores an asset under the given directory -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil -} - -// RestoreAssets restores an asset under the given directory recursively -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) -} diff --git a/_examples/view/template_pug_3/main.go b/_examples/view/template_pug_3/main.go deleted file mode 100644 index 68b05f4284..0000000000 --- a/_examples/view/template_pug_3/main.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import "github.com/kataras/iris/v12" - -// $ go get -u github.com/go-bindata/go-bindata/v3/go-bindata -// $ go-bindata ./templates/... -// $ go build -func main() { - app := iris.New() - - tmpl := iris.Pug("./templates", ".pug").Binary(Asset, AssetNames) - - app.RegisterView(tmpl) - - app.Get("/", index) - - // http://localhost:8080 - app.Listen(":8080") -} - -func index(ctx iris.Context) { - ctx.View("index.pug") -} diff --git a/_examples/view/template_pug_3/templates/index.pug b/_examples/view/template_pug_3/templates/index.pug deleted file mode 100644 index f061806c2b..0000000000 --- a/_examples/view/template_pug_3/templates/index.pug +++ /dev/null @@ -1,7 +0,0 @@ -extends layout.pug - -block title - title Article Title - -block content - h1 My Article \ No newline at end of file diff --git a/_examples/view/template_pug_3/templates/layout.pug b/_examples/view/template_pug_3/templates/layout.pug deleted file mode 100644 index ebd199f32d..0000000000 --- a/_examples/view/template_pug_3/templates/layout.pug +++ /dev/null @@ -1,7 +0,0 @@ -doctype html -html - head - block title - title Default title - body - block content \ No newline at end of file diff --git a/_examples/view/write-to/main.go b/_examples/view/write-to/main.go deleted file mode 100644 index 438c101f27..0000000000 --- a/_examples/view/write-to/main.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "os" - - "github.com/kataras/iris/v12" -) - -type mailData struct { - Title string - Body string - RefTitle string - RefLink string -} - -func main() { - app := iris.New() - app.Logger().SetLevel("debug") - app.RegisterView(iris.HTML("./views", ".html")) - - // you need to call `app.Build` manually before using the `app.View` func, - // so templates are built in that state. - app.Build() - - // Or a string-buffered writer to use its body to send an e-mail - // for sending e-mails you can use the https://github.com/kataras/go-mailer - // or any other third-party package you like. - // - // The template's parsed result will be written to that writer. - writer := os.Stdout - err := app.View(writer, "email/simple.html", "shared/email.html", mailData{ - Title: "This is my e-mail title", - Body: "This is my e-mail body", - RefTitle: "Iris web framework", - RefLink: "https://iris-go.com", - }) - if err != nil { - app.Logger().Errorf("error from app.View: %v", err) - } - - app.Listen(":8080") -} diff --git a/_examples/view/write-to/views/email/simple.html b/_examples/view/write-to/views/email/simple.html deleted file mode 100644 index 6e411194e0..0000000000 --- a/_examples/view/write-to/views/email/simple.html +++ /dev/null @@ -1 +0,0 @@ -{{.Body}} \ No newline at end of file diff --git a/_examples/view/write-to/views/shared/email.html b/_examples/view/write-to/views/shared/email.html deleted file mode 100644 index 6d5baceb02..0000000000 --- a/_examples/view/write-to/views/shared/email.html +++ /dev/null @@ -1,6 +0,0 @@ -

    {{.Title}}

    -

    - {{yield}} -

    - -{{.RefTitle}} diff --git a/_examples/webassembly/client/go-wasm-runtime.js b/_examples/webassembly/client/go-wasm-runtime.js deleted file mode 100644 index ecb096509f..0000000000 --- a/_examples/webassembly/client/go-wasm-runtime.js +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -(() => { - // Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API). - const isNodeJS = typeof process !== "undefined"; - if (isNodeJS) { - global.require = require; - global.fs = require("fs"); - - const nodeCrypto = require("crypto"); - global.crypto = { - getRandomValues(b) { - nodeCrypto.randomFillSync(b); - }, - }; - - global.performance = { - now() { - const [sec, nsec] = process.hrtime(); - return sec * 1000 + nsec / 1000000; - }, - }; - - const util = require("util"); - global.TextEncoder = util.TextEncoder; - global.TextDecoder = util.TextDecoder; - } else { - window.global = window; - - let outputBuf = ""; - global.fs = { - constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_NONBLOCK: -1, O_SYNC: -1 }, // unused - writeSync(fd, buf) { - outputBuf += decoder.decode(buf); - const nl = outputBuf.lastIndexOf("\n"); - if (nl != -1) { - console.log(outputBuf.substr(0, nl)); - outputBuf = outputBuf.substr(nl + 1); - } - return buf.length; - }, - openSync(path, flags, mode) { - const err = new Error("not implemented"); - err.code = "ENOSYS"; - throw err; - }, - }; - } - - const encoder = new TextEncoder("utf-8"); - const decoder = new TextDecoder("utf-8"); - - global.Go = class { - constructor() { - this.argv = ["js"]; - this.env = {}; - this.exit = (code) => { - if (code !== 0) { - console.warn("exit code:", code); - } - }; - this._callbackTimeouts = new Map(); - this._nextCallbackTimeoutID = 1; - - const mem = () => { - // The buffer may change when requesting more memory. - return new DataView(this._inst.exports.mem.buffer); - } - - const setInt64 = (addr, v) => { - mem().setUint32(addr + 0, v, true); - mem().setUint32(addr + 4, Math.floor(v / 4294967296), true); - } - - const getInt64 = (addr) => { - const low = mem().getUint32(addr + 0, true); - const high = mem().getInt32(addr + 4, true); - return low + high * 4294967296; - } - - const loadValue = (addr) => { - const f = mem().getFloat64(addr, true); - if (!isNaN(f)) { - return f; - } - - const id = mem().getUint32(addr, true); - return this._values[id]; - } - - const storeValue = (addr, v) => { - if (typeof v === "number") { - if (isNaN(v)) { - mem().setUint32(addr + 4, 0x7FF80000, true); // NaN - mem().setUint32(addr, 0, true); - return; - } - mem().setFloat64(addr, v, true); - return; - } - - mem().setUint32(addr + 4, 0x7FF80000, true); // NaN - - switch (v) { - case undefined: - mem().setUint32(addr, 1, true); - return; - case null: - mem().setUint32(addr, 2, true); - return; - case true: - mem().setUint32(addr, 3, true); - return; - case false: - mem().setUint32(addr, 4, true); - return; - } - - if (typeof v === "string") { - let ref = this._stringRefs.get(v); - if (ref === undefined) { - ref = this._values.length; - this._values.push(v); - this._stringRefs.set(v, ref); - } - mem().setUint32(addr, ref, true); - return; - } - - if (typeof v === "symbol") { - let ref = this._symbolRefs.get(v); - if (ref === undefined) { - ref = this._values.length; - this._values.push(v); - this._symbolRefs.set(v, ref); - } - mem().setUint32(addr, ref, true); - return; - } - - let ref = v[this._refProp]; - if (ref === undefined) { - ref = this._values.length; - this._values.push(v); - v[this._refProp] = ref; - } - mem().setUint32(addr, ref, true); - } - - const loadSlice = (addr) => { - const array = getInt64(addr + 0); - const len = getInt64(addr + 8); - return new Uint8Array(this._inst.exports.mem.buffer, array, len); - } - - const loadSliceOfValues = (addr) => { - const array = getInt64(addr + 0); - const len = getInt64(addr + 8); - const a = new Array(len); - for (let i = 0; i < len; i++) { - a[i] = loadValue(array + i * 8); - } - return a; - } - - const loadString = (addr) => { - const saddr = getInt64(addr + 0); - const len = getInt64(addr + 8); - return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); - } - - const timeOrigin = Date.now() - performance.now(); - this.importObject = { - go: { - // func wasmExit(code int32) - "runtime.wasmExit": (sp) => { - this.exited = true; - this.exit(mem().getInt32(sp + 8, true)); - }, - - // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) - "runtime.wasmWrite": (sp) => { - const fd = getInt64(sp + 8); - const p = getInt64(sp + 16); - const n = mem().getInt32(sp + 24, true); - fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); - }, - - // func nanotime() int64 - "runtime.nanotime": (sp) => { - setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); - }, - - // func walltime() (sec int64, nsec int32) - "runtime.walltime": (sp) => { - const msec = (new Date).getTime(); - setInt64(sp + 8, msec / 1000); - mem().setInt32(sp + 16, (msec % 1000) * 1000000, true); - }, - - // func scheduleCallback(delay int64) int32 - "runtime.scheduleCallback": (sp) => { - const id = this._nextCallbackTimeoutID; - this._nextCallbackTimeoutID++; - this._callbackTimeouts.set(id, setTimeout( - () => { this._resolveCallbackPromise(); }, - getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early - )); - mem().setInt32(sp + 16, id, true); - }, - - // func clearScheduledCallback(id int32) - "runtime.clearScheduledCallback": (sp) => { - const id = mem().getInt32(sp + 8, true); - clearTimeout(this._callbackTimeouts.get(id)); - this._callbackTimeouts.delete(id); - }, - - // func getRandomData(r []byte) - "runtime.getRandomData": (sp) => { - crypto.getRandomValues(loadSlice(sp + 8)); - }, - - // func stringVal(value string) ref - "syscall/js.stringVal": (sp) => { - storeValue(sp + 24, loadString(sp + 8)); - }, - - // func valueGet(v ref, p string) ref - "syscall/js.valueGet": (sp) => { - storeValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16))); - }, - - // func valueSet(v ref, p string, x ref) - "syscall/js.valueSet": (sp) => { - Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); - }, - - // func valueIndex(v ref, i int) ref - "syscall/js.valueIndex": (sp) => { - storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); - }, - - // valueSetIndex(v ref, i int, x ref) - "syscall/js.valueSetIndex": (sp) => { - Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); - }, - - // func valueCall(v ref, m string, args []ref) (ref, bool) - "syscall/js.valueCall": (sp) => { - try { - const v = loadValue(sp + 8); - const m = Reflect.get(v, loadString(sp + 16)); - const args = loadSliceOfValues(sp + 32); - storeValue(sp + 56, Reflect.apply(m, v, args)); - mem().setUint8(sp + 64, 1); - } catch (err) { - storeValue(sp + 56, err); - mem().setUint8(sp + 64, 0); - } - }, - - // func valueInvoke(v ref, args []ref) (ref, bool) - "syscall/js.valueInvoke": (sp) => { - try { - const v = loadValue(sp + 8); - const args = loadSliceOfValues(sp + 16); - storeValue(sp + 40, Reflect.apply(v, undefined, args)); - mem().setUint8(sp + 48, 1); - } catch (err) { - storeValue(sp + 40, err); - mem().setUint8(sp + 48, 0); - } - }, - - // func valueNew(v ref, args []ref) (ref, bool) - "syscall/js.valueNew": (sp) => { - try { - const v = loadValue(sp + 8); - const args = loadSliceOfValues(sp + 16); - storeValue(sp + 40, Reflect.construct(v, args)); - mem().setUint8(sp + 48, 1); - } catch (err) { - storeValue(sp + 40, err); - mem().setUint8(sp + 48, 0); - } - }, - - // func valueLength(v ref) int - "syscall/js.valueLength": (sp) => { - setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); - }, - - // valuePrepareString(v ref) (ref, int) - "syscall/js.valuePrepareString": (sp) => { - const str = encoder.encode(String(loadValue(sp + 8))); - storeValue(sp + 16, str); - setInt64(sp + 24, str.length); - }, - - // valueLoadString(v ref, b []byte) - "syscall/js.valueLoadString": (sp) => { - const str = loadValue(sp + 8); - loadSlice(sp + 16).set(str); - }, - - // func valueInstanceOf(v ref, t ref) bool - "syscall/js.valueInstanceOf": (sp) => { - mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16)); - }, - - "debug": (value) => { - console.log(value); - }, - } - }; - } - - async run(instance) { - this._inst = instance; - this._values = [ // TODO: garbage collection - NaN, - undefined, - null, - true, - false, - global, - this._inst.exports.mem, - () => { // resolveCallbackPromise - if (this.exited) { - throw new Error("bad callback: Go program has already exited"); - } - setTimeout(this._resolveCallbackPromise, 0); // make sure it is asynchronous - }, - ]; - this._stringRefs = new Map(); - this._symbolRefs = new Map(); - this._refProp = Symbol(); - this.exited = false; - - const mem = new DataView(this._inst.exports.mem.buffer) - - // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. - let offset = 4096; - - const strPtr = (str) => { - let ptr = offset; - new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0")); - offset += str.length + (8 - (str.length % 8)); - return ptr; - }; - - const argc = this.argv.length; - - const argvPtrs = []; - this.argv.forEach((arg) => { - argvPtrs.push(strPtr(arg)); - }); - - const keys = Object.keys(this.env).sort(); - argvPtrs.push(keys.length); - keys.forEach((key) => { - argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); - }); - - const argv = offset; - argvPtrs.forEach((ptr) => { - mem.setUint32(offset, ptr, true); - mem.setUint32(offset + 4, 0, true); - offset += 8; - }); - - while (true) { - const callbackPromise = new Promise((resolve) => { - this._resolveCallbackPromise = resolve; - }); - this._inst.exports.run(argc, argv); - if (this.exited) { - break; - } - await callbackPromise; - } - } - } - - if (isNodeJS) { - if (process.argv.length < 3) { - process.stderr.write("usage: go_js_wasm_exec [wasm binary] [arguments]\n"); - process.exit(1); - } - - const go = new Go(); - go.argv = process.argv.slice(2); - go.env = process.env; - go.exit = process.exit; - WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { - process.on("exit", () => { // Node.js exits if no callback is pending - if (!go.exited) { - console.error("error: all goroutines asleep and no JavaScript callback pending - deadlock!"); - process.exit(1); - } - }); - return go.run(result.instance); - }).catch((err) => { - console.error(err); - go.exited = true; - process.exit(1); - }); - } -})(); diff --git a/_examples/webassembly/client/hello.html b/_examples/webassembly/client/hello.html deleted file mode 100644 index 29caa55b7d..0000000000 --- a/_examples/webassembly/client/hello.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - Hello Webassembly + Iris (Go) - - -
    - - - \ No newline at end of file diff --git a/_examples/webassembly/client/hello_go114.go b/_examples/webassembly/client/hello_go114.go deleted file mode 100644 index 83e0413ffa..0000000000 --- a/_examples/webassembly/client/hello_go114.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build js - -package main - -import ( - "fmt" - "syscall/js" - "time" -) - -func main() { - // GOARCH=wasm GOOS=js /home/$yourusername/go1.14/bin/go build -o hello.wasm hello_go114.go - js.Global().Get("console").Call("log", "Hello Webassembly!") - message := fmt.Sprintf("Hello, the current time is: %s", time.Now().String()) - js.Global().Get("document").Call("getElementById", "hello").Set("innerText", message) -} diff --git a/_examples/webassembly/client/main.js b/_examples/webassembly/client/main.js deleted file mode 100644 index 02fac56ade..0000000000 --- a/_examples/webassembly/client/main.js +++ /dev/null @@ -1,13 +0,0 @@ -import './go-wasm-runtime.js'; - -if (!WebAssembly.instantiateStreaming) { // polyfill - WebAssembly.instantiateStreaming = async (resp, importObject) => { - const source = await (await resp).arrayBuffer(); - return await WebAssembly.instantiate(source, importObject); - }; -} - -const go = new Go(); -WebAssembly.instantiateStreaming(fetch("hello.wasm"), go.importObject).then((result) => { - return WebAssembly.instantiate(result.module, go.importObject); -}).then(instance => go.run(instance)); \ No newline at end of file diff --git a/_examples/webassembly/main.go b/_examples/webassembly/main.go deleted file mode 100644 index 6d611da1f7..0000000000 --- a/_examples/webassembly/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "github.com/kataras/iris/v12" -) - -/* -You need to build the hello.wasm first, download the go1.14 and execute the below command: -$ cd client && GOARCH=wasm GOOS=js /home/$yourname/go1.14/bin/go build -o hello.wasm hello_go114.go -*/ - -func main() { - app := iris.New() - - // we could serve your assets like this the sake of the example, - // never include the .go files there in production. - app.HandleDir("/", iris.Dir("./client")) - - app.Get("/", func(ctx iris.Context) { - // ctx.CompressWriter(true) - ctx.ServeFile("./client/hello.html") - }) - - // visit http://localhost:8080 - // you should get an html output like this: - // Hello, the current time is: 2018-07-09 05:54:12.564 +0000 UTC m=+0.003900161 - app.Listen(":8080") -} diff --git a/_examples/websocket/README.md b/_examples/websocket/README.md deleted file mode 100644 index 8c8d99675b..0000000000 --- a/_examples/websocket/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Websocket - -[WebSocket](https://wikipedia.org/wiki/WebSocket) is a protocol that enables two-way persistent communication channels over TCP connections. It is used for applications such as chat, stock tickers, games, anywhere you want real-time functionality in a web application. - -Iris websocket library is now merged with the [neffos real-time framework](https://github.com/kataras/neffos) and Iris-specific helpers and type aliases live on the [iris/websocket](https://github.com/kataras/iris/tree/master/websocket) subpackage. Learn neffos from its [wiki](https://github.com/kataras/neffos#learning-neffos). - -Helpers and type aliases improves your code speed when writing a websocket module. -For example, instead of importing both `kataras/iris/websocket` - in order to use its `websocket.Handler` - and `github.com/kataras/neffos` - to create a new websocket server `neffos.New` - you can use the `websocket.New` instead, another example is the `neffos.Conn` which can be declared as `websocket.Conn`. - -All neffos and its subpackage's types and package-level functions exist as type aliases on the `kataras/iris/websocket` package too, there are too many of those and there is no need to write each one of those here, some common types: - -- `github.com/kataras/neffos/#Conn` -> `github.com/kataras/iris/websocket/#Conn` -- `github.com/kataras/neffos/gorilla/#DefaultUpgrader` -> `github.com/kataras/iris/websocket/#DefaultGorillaUpgrader` -- `github.com/kataras/neffos/stackexchange/redis/#NewStackExchange` -> `github.com/kataras/iris/websocket/#NewRedisStackExchange` diff --git a/_examples/websocket/basic/README.md b/_examples/websocket/basic/README.md deleted file mode 100644 index 948b18bcd6..0000000000 --- a/_examples/websocket/basic/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Basic Example - -At the end of this example you will be able to run a websocket server -and clients for all platforms (Go, Browser and Nodejs). - -![](overview.png) - -Open as many clients as you want to try out and start typing. - -This example contains only the basics, however, the library supports rooms, native websocket messages, any data can be sent and received (i.e protobufs, json) and all kinds of broadcasting and connections collections. - -## How to run - -### Server - -Open a terminal window instance and execute: - -```sh -$ go run server.go # start the websocket server. -``` - -### Client (Go) - -Start a new terminal instance and execute: - -```sh -$ cd ./go-client -$ go run client.go # start the websocket client. -# start typing... -``` - -### Client (Browser) - -Navigate to and start typing. -The `./browser/index.html` should be served, it contains the client-side code. - -### Client (Browserify) - -Install [NPM](https://nodejs.org) first, then start a new terminal instance and execute: - -```sh -$ cd ./browserify -$ npm install -# build the modern browser-side client: -# embed the neffos.js node-module and app.js -# into a single ./browserify/bundle.js file -# which ./browserify/client.html imports. -$ npm run-script build -``` - -Navigate to and start typing. - -### Client (Nodejs) - -Install [NPM](https://nodejs.org) if you haven't already and then, start a new terminal instance and execute: - -```sh -$ cd nodejs-client -$ npm install -$ node client.js # start the websocket client. -# start typing. -``` diff --git a/_examples/websocket/basic/browser/index.html b/_examples/websocket/basic/browser/index.html deleted file mode 100644 index 493b79875e..0000000000 --- a/_examples/websocket/basic/browser/index.html +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - -
    
    -
    -
    -
    \ No newline at end of file
    diff --git a/_examples/websocket/basic/browserify/app.js b/_examples/websocket/basic/browserify/app.js
    deleted file mode 100644
    index 8acfb9c947..0000000000
    --- a/_examples/websocket/basic/browserify/app.js
    +++ /dev/null
    @@ -1,75 +0,0 @@
    -const neffos = require('neffos.js');
    -
    -var scheme = document.location.protocol == "https:" ? "wss" : "ws";
    -var port = document.location.port ? ":" + document.location.port : "";
    -
    -var wsURL = scheme + "://" + document.location.hostname + port + "/echo";
    -
    -const enableJWT = true;
    -if (enableJWT) {
    -  // This is just a signature and a payload of an example content, 
    -  // please replace this with your logic.
    -  //
    -  // Add a random letter in front of the token to make it
    -  // invalid and see that this client is not allowed to dial the websocket server.
    -  const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjozMjEzMjF9.8waEX7-vPKACa-Soi1pQvW3Rl8QY-SUFcHKTLZI4mvU";
    -  wsURL += "?token=" + token;
    -}
    -
    -var outputTxt = document.getElementById("output");
    -
    -function addMessage(msg) {
    -  outputTxt.innerHTML += msg + "\n";
    -}
    -
    -function handleError(reason) {
    -  console.log(reason);
    -  window.alert(reason);
    -}
    -
    -function handleNamespaceConnectedConn(nsConn) {
    -  nsConn.emit("chat", "Hello from browser(ify) client-side!");
    -
    -  const inputTxt = document.getElementById("input");
    -  const sendBtn = document.getElementById("sendBtn");
    -
    -  sendBtn.disabled = false;
    -  sendBtn.onclick = function () {
    -    const input = inputTxt.value;
    -    inputTxt.value = "";
    -
    -    nsConn.emit("chat", input);
    -    addMessage("Me: " + input);
    -  };
    -}
    -
    -async function runExample() {
    -  try {
    -    const conn = await neffos.dial(wsURL, {
    -      default: { // "default" namespace.
    -        _OnNamespaceConnected: function (nsConn, msg) {
    -          addMessage("connected to namespace: " + msg.Namespace);
    -          handleNamespaceConnectedConn(nsConn);
    -        },
    -        _OnNamespaceDisconnect: function (nsConn, msg) {
    -          addMessage("disconnected from namespace: " + msg.Namespace);
    -        },
    -        chat: function (nsConn, msg) { // "chat" event.
    -          addMessage(msg.Body);
    -        }
    -      }
    -    });
    -
    -    // You can either wait to conenct or just conn.connect("connect")
    -    // and put the `handleNamespaceConnectedConn` inside `_OnNamespaceConnected` callback instead.
    -    // const nsConn = await conn.connect("default");
    -    // handleNamespaceConnectedConn(nsConn);
    -    // nsConn.emit(...); handleNamespaceConnectedConn(nsConn);
    -    conn.connect("default");
    -
    -  } catch (err) {
    -    handleError(err);
    -  }
    -}
    -
    -runExample();
    \ No newline at end of file
    diff --git a/_examples/websocket/basic/browserify/bundle.js b/_examples/websocket/basic/browserify/bundle.js
    deleted file mode 100644
    index 9530f82c9f..0000000000
    --- a/_examples/websocket/basic/browserify/bundle.js
    +++ /dev/null
    @@ -1 +0,0 @@
    -(function(){function b(d,e,g){function a(j,i){if(!e[j]){if(!d[j]){var f="function"==typeof require&&require;if(!i&&f)return f(j,!0);if(h)return h(j,!0);var c=new Error("Cannot find module '"+j+"'");throw c.code="MODULE_NOT_FOUND",c}var k=e[j]={exports:{}};d[j][0].call(k.exports,function(b){var c=d[j][1][b];return a(c||b)},k,k.exports,b,d,e,g)}return e[j].exports}for(var h="function"==typeof require&&require,c=0;c=i)return k.close(),null;var c=new Map;k.connectedNamespaces.forEach(function(a,b){var d=[];!q(a.rooms)&&0i[0]&&c[1]
    -
    -
    -
    -
    -
    -
    -
    
    -
    -
    diff --git a/_examples/websocket/basic/browserify/package.json b/_examples/websocket/basic/browserify/package.json
    deleted file mode 100644
    index de8513673c..0000000000
    --- a/_examples/websocket/basic/browserify/package.json
    +++ /dev/null
    @@ -1,16 +0,0 @@
    -{
    -    "name": "neffos.js.example.browserify",
    -    "version": "0.0.1",
    -    "scripts": {
    -        "browserify": "browserify ./app.js -o ./bundle.js",
    -        "minifyES6": "minify ./bundle.js --outFile ./bundle.js",
    -        "build": "npm run-script browserify && npm run-script minifyES6"
    -    },
    -    "dependencies": {
    -        "neffos.js": "latest"
    -    },
    -    "devDependencies": {
    -        "browserify": "^16.2.3",
    -        "babel-minify": "^0.5.0"
    -    }
    -}
    diff --git a/_examples/websocket/basic/go-client/client.go b/_examples/websocket/basic/go-client/client.go
    deleted file mode 100644
    index 0546488737..0000000000
    --- a/_examples/websocket/basic/go-client/client.go
    +++ /dev/null
    @@ -1,85 +0,0 @@
    -package main
    -
    -import (
    -	"bufio"
    -	"bytes"
    -	"context"
    -	"fmt"
    -	"log"
    -	"os"
    -	"time"
    -
    -	"github.com/kataras/iris/v12/websocket"
    -)
    -
    -const (
    -	endpoint              = "ws://localhost:8080/echo"
    -	namespace             = "default"
    -	dialAndConnectTimeout = 5 * time.Second
    -)
    -
    -// this can be shared with the server.go's.
    -// `NSConn.Conn` has the `IsClient() bool` method which can be used to
    -// check if that's is a client or a server-side callback.
    -var clientEvents = websocket.Namespaces{
    -	namespace: websocket.Events{
    -		websocket.OnNamespaceConnected: func(c *websocket.NSConn, msg websocket.Message) error {
    -			log.Printf("connected to namespace: %s", msg.Namespace)
    -			return nil
    -		},
    -		websocket.OnNamespaceDisconnect: func(c *websocket.NSConn, msg websocket.Message) error {
    -			log.Printf("disconnected from namespace: %s", msg.Namespace)
    -			return nil
    -		},
    -		"chat": func(c *websocket.NSConn, msg websocket.Message) error {
    -			log.Printf("%s", string(msg.Body))
    -			return nil
    -		},
    -	},
    -}
    -
    -func main() {
    -	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(dialAndConnectTimeout))
    -	defer cancel()
    -
    -	// username := "my_username"
    -	// dialer := websocket.GobwasDialer(websocket.GobwasDialerOptions{Header: websocket.GobwasHeader{"X-Username": []string{username}}})
    -	dialer := websocket.DefaultGobwasDialer
    -	client, err := websocket.Dial(ctx, dialer, endpoint, clientEvents)
    -	if err != nil {
    -		panic(err)
    -	}
    -	defer client.Close()
    -
    -	c, err := client.Connect(ctx, namespace)
    -	if err != nil {
    -		panic(err)
    -	}
    -
    -	c.Emit("chat", []byte("Hello from Go client side!"))
    -
    -	fmt.Fprint(os.Stdout, ">> ")
    -	scanner := bufio.NewScanner(os.Stdin)
    -	for {
    -		if !scanner.Scan() {
    -			log.Printf("ERROR: %v", scanner.Err())
    -			return
    -		}
    -
    -		text := scanner.Bytes()
    -
    -		if bytes.Equal(text, []byte("exit")) {
    -			if err := c.Disconnect(nil); err != nil {
    -				log.Printf("reply from server: %v", err)
    -			}
    -			break
    -		}
    -
    -		ok := c.Emit("chat", text)
    -		if !ok {
    -			break
    -		}
    -
    -		fmt.Fprint(os.Stdout, ">> ")
    -	}
    -} // try running this program twice or/and run the server's http://localhost:8080 to check the browser client as well.
    diff --git a/_examples/websocket/basic/go.mod b/_examples/websocket/basic/go.mod
    deleted file mode 100644
    index b126c8a5df..0000000000
    --- a/_examples/websocket/basic/go.mod
    +++ /dev/null
    @@ -1,5 +0,0 @@
    -module github.com/kataras/iris/_examples/websocket/basic
    -
    -go 1.15
    -
    -require github.com/iris-contrib/middleware/jwt v0.0.0-20200710202437-92b01b85baaf
    \ No newline at end of file
    diff --git a/_examples/websocket/basic/nodejs-client/client.js b/_examples/websocket/basic/nodejs-client/client.js
    deleted file mode 100644
    index fd4612ef5c..0000000000
    --- a/_examples/websocket/basic/nodejs-client/client.js
    +++ /dev/null
    @@ -1,35 +0,0 @@
    -const neffos = require('neffos.js');
    -const stdin = process.openStdin();
    -
    -const wsURL = "ws://localhost:8080/echo";
    -
    -async function runExample() {
    -  try {
    -    const conn = await neffos.dial(wsURL, {
    -      default: { // "default" namespace.
    -        _OnNamespaceConnected: function (nsConn, msg) {
    -          console.log("connected to namespace: " + msg.Namespace);
    -        },
    -        _OnNamespaceDisconnect: function (nsConn, msg) {
    -          console.log("disconnected from namespace: " + msg.Namespace);
    -        },
    -        chat: function (nsConn, msg) { // "chat" event.
    -          console.log(msg.Body);
    -        }
    -      }
    -    });
    -
    -    const nsConn = await conn.connect("default");
    -    nsConn.emit("chat", "Hello from Nodejs client side!");
    -
    -    stdin.addListener("data", function (data) {
    -      const text = data.toString().trim();
    -      nsConn.emit("chat", text);
    -    });
    -
    -  } catch (err) {
    -    console.error(err);
    -  }
    -}
    -
    -runExample();
    diff --git a/_examples/websocket/basic/nodejs-client/package.json b/_examples/websocket/basic/nodejs-client/package.json
    deleted file mode 100644
    index 58540d075b..0000000000
    --- a/_examples/websocket/basic/nodejs-client/package.json
    +++ /dev/null
    @@ -1,8 +0,0 @@
    -{
    -    "name": "neffos.js.example.nodejsclient",
    -    "version": "0.0.1",
    -    "main": "client.js",
    -    "dependencies": {
    -        "neffos.js": "latest"
    -    }
    -}
    diff --git a/_examples/websocket/basic/overview.png b/_examples/websocket/basic/overview.png
    deleted file mode 100644
    index 69afa9d3ef..0000000000
    Binary files a/_examples/websocket/basic/overview.png and /dev/null differ
    diff --git a/_examples/websocket/basic/server.go b/_examples/websocket/basic/server.go
    deleted file mode 100644
    index c67813da27..0000000000
    --- a/_examples/websocket/basic/server.go
    +++ /dev/null
    @@ -1,120 +0,0 @@
    -package main
    -
    -import (
    -	"log"
    -
    -	"github.com/kataras/iris/v12"
    -	"github.com/kataras/iris/v12/websocket"
    -
    -	// Used when "enableJWT" constant is true:
    -	"github.com/iris-contrib/middleware/jwt"
    -)
    -
    -// values should match with the client sides as well.
    -const enableJWT = true
    -const namespace = "default"
    -
    -// if namespace is empty then simply websocket.Events{...} can be used instead.
    -var serverEvents = websocket.Namespaces{
    -	namespace: websocket.Events{
    -		websocket.OnNamespaceConnected: func(nsConn *websocket.NSConn, msg websocket.Message) error {
    -			// with `websocket.GetContext` you can retrieve the Iris' `Context`.
    -			ctx := websocket.GetContext(nsConn.Conn)
    -
    -			log.Printf("[%s] connected to namespace [%s] with IP [%s]",
    -				nsConn, msg.Namespace,
    -				ctx.RemoteAddr())
    -			return nil
    -		},
    -		websocket.OnNamespaceDisconnect: func(nsConn *websocket.NSConn, msg websocket.Message) error {
    -			log.Printf("[%s] disconnected from namespace [%s]", nsConn, msg.Namespace)
    -			return nil
    -		},
    -		"chat": func(nsConn *websocket.NSConn, msg websocket.Message) error {
    -			// room.String() returns -> NSConn.String() returns -> Conn.String() returns -> Conn.ID()
    -			log.Printf("[%s] sent: %s", nsConn, string(msg.Body))
    -
    -			// Write message back to the client message owner with:
    -			// nsConn.Emit("chat", msg)
    -			// Write message to all except this client with:
    -			nsConn.Conn.Server().Broadcast(nsConn, msg)
    -			return nil
    -		},
    -	},
    -}
    -
    -func main() {
    -	app := iris.New()
    -	websocketServer := websocket.New(
    -		websocket.DefaultGorillaUpgrader, /* DefaultGobwasUpgrader can be used too. */
    -		serverEvents)
    -
    -	j := jwt.New(jwt.Config{
    -		// Extract by the "token" url,
    -		// so the client should dial with ws://localhost:8080/echo?token=$token
    -		Extractor: jwt.FromParameter("token"),
    -
    -		ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
    -			return []byte("My Secret"), nil
    -		},
    -
    -		// When set, the middleware verifies that tokens are signed
    -		// with the specific signing algorithm
    -		// If the signing method is not constant the
    -		// `Config.ValidationKeyGetter` callback field can be used
    -		// to implement additional checks
    -		// Important to avoid security issues described here:
    -		// https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
    -		SigningMethod: jwt.SigningMethodHS256,
    -	})
    -
    -	idGen := func(ctx iris.Context) string {
    -		if username := ctx.GetHeader("X-Username"); username != "" {
    -			return username
    -		}
    -
    -		return websocket.DefaultIDGenerator(ctx)
    -	}
    -
    -	// serves the endpoint of ws://localhost:8080/echo
    -	// with optional custom ID generator.
    -	websocketRoute := app.Get("/echo", websocket.Handler(websocketServer, idGen))
    -
    -	if enableJWT {
    -		// Register the jwt middleware (on handshake):
    -		websocketRoute.Use(j.Serve)
    -		// OR
    -		//
    -		// Check for token through the jwt middleware
    -		// on websocket connection or on any event:
    -		/* websocketServer.OnConnect = func(c *websocket.Conn) error {
    -		ctx := websocket.GetContext(c)
    -		if err := j.CheckJWT(ctx); err != nil {
    -			// will send the above error on the client
    -			// and will not allow it to connect to the websocket server at all.
    -			return err
    -		}
    -
    -		user := ctx.Values().Get("jwt").(*jwt.Token)
    -		// or just: user := j.Get(ctx)
    -
    -		log.Printf("This is an authenticated request\n")
    -		log.Printf("Claim content:")
    -		log.Printf("%#+v\n", user.Claims)
    -
    -		log.Printf("[%s] connected to the server", c.ID())
    -
    -		return nil
    -		} */
    -	}
    -
    -	// serves the browser-based websocket client.
    -	app.Get("/", func(ctx iris.Context) {
    -		ctx.ServeFile("./browser/index.html")
    -	})
    -
    -	// serves the npm browser websocket client usage example.
    -	app.HandleDir("/browserify", iris.Dir("./browserify"))
    -
    -	app.Listen(":8080")
    -}
    diff --git a/_examples/websocket/gorilla-filewatch/go.mod b/_examples/websocket/gorilla-filewatch/go.mod
    deleted file mode 100644
    index 57c2ff69ea..0000000000
    --- a/_examples/websocket/gorilla-filewatch/go.mod
    +++ /dev/null
    @@ -1,10 +0,0 @@
    -module gorilla-filewatch-example
    -
    -go 1.15
    -
    -require (
    -	github.com/gorilla/websocket v1.4.2
    -	github.com/kataras/iris/v12 v12.1.8
    -)
    -
    -replace github.com/kataras/iris/v12 => ../../../
    diff --git a/_examples/websocket/gorilla-filewatch/main.go b/_examples/websocket/gorilla-filewatch/main.go
    deleted file mode 100644
    index e740c70728..0000000000
    --- a/_examples/websocket/gorilla-filewatch/main.go
    +++ /dev/null
    @@ -1,168 +0,0 @@
    -// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
    -// Use of this source code is governed by a BSD-style
    -// license that can be found in the LICENSE file.
    -//
    -// This example shows a use case with gorilla webscokets package
    -// sends a file to the browser client for display whenever the file is modified.
    -//
    -// $ go run main.go 
    -// # Open http://localhost:8080/ .
    -// # Modify the file to see it update in the browser.
    -package main
    -
    -import (
    -	"flag"
    -	"io/ioutil"
    -	"log"
    -	"os"
    -	"strconv"
    -	"time"
    -
    -	"github.com/gorilla/websocket"
    -	"github.com/kataras/iris/v12"
    -)
    -
    -const (
    -	// Time allowed to write the file to the client.
    -	writeWait = 10 * time.Second
    -
    -	// Time allowed to read the next pong message from the client.
    -	pongWait = 60 * time.Second
    -
    -	// Send pings to client with this period. Must be less than pongWait.
    -	pingPeriod = (pongWait * 9) / 10
    -
    -	// Poll file for changes with this period.
    -	filePeriod = 10 * time.Second
    -)
    -
    -var (
    -	addr     = flag.String("addr", ":8080", "http service address")
    -	filename string
    -	upgrader = websocket.Upgrader{
    -		ReadBufferSize:  1024,
    -		WriteBufferSize: 1024,
    -	}
    -)
    -
    -func readFileIfModified(lastMod time.Time) ([]byte, time.Time, error) {
    -	fi, err := os.Stat(filename)
    -	if err != nil {
    -		return nil, lastMod, err
    -	}
    -	if !fi.ModTime().After(lastMod) {
    -		return nil, lastMod, nil
    -	}
    -	p, err := ioutil.ReadFile(filename)
    -	if err != nil {
    -		return nil, fi.ModTime(), err
    -	}
    -	return p, fi.ModTime(), nil
    -}
    -
    -func reader(ws *websocket.Conn) {
    -	defer ws.Close()
    -	ws.SetReadLimit(512)
    -	ws.SetReadDeadline(time.Now().Add(pongWait))
    -	ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
    -	for {
    -		_, _, err := ws.ReadMessage()
    -		if err != nil {
    -			break
    -		}
    -	}
    -}
    -
    -func writer(ws *websocket.Conn, lastMod time.Time) {
    -	lastError := ""
    -	pingTicker := time.NewTicker(pingPeriod)
    -	fileTicker := time.NewTicker(filePeriod)
    -	defer func() {
    -		pingTicker.Stop()
    -		fileTicker.Stop()
    -		ws.Close()
    -	}()
    -	for {
    -		select {
    -		case <-fileTicker.C:
    -			var p []byte
    -			var err error
    -
    -			p, lastMod, err = readFileIfModified(lastMod)
    -
    -			if err != nil {
    -				if s := err.Error(); s != lastError {
    -					lastError = s
    -					p = []byte(lastError)
    -				}
    -			} else {
    -				lastError = ""
    -			}
    -
    -			if p != nil {
    -				ws.SetWriteDeadline(time.Now().Add(writeWait))
    -				if err := ws.WriteMessage(websocket.TextMessage, p); err != nil {
    -					return
    -				}
    -			}
    -		case <-pingTicker.C:
    -			ws.SetWriteDeadline(time.Now().Add(writeWait))
    -			if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
    -				return
    -			}
    -		}
    -	}
    -}
    -
    -func serveWs(ctx iris.Context) {
    -	ws, err := upgrader.Upgrade(ctx.ResponseWriter(), ctx.Request(), nil)
    -	if err != nil {
    -		if _, ok := err.(websocket.HandshakeError); !ok {
    -			log.Println(err)
    -		}
    -		return
    -	}
    -
    -	var lastMod time.Time
    -	if n, err := strconv.ParseInt(ctx.FormValue("lastMod"), 16, 64); err == nil {
    -		lastMod = time.Unix(0, n)
    -	}
    -
    -	go writer(ws, lastMod)
    -	reader(ws)
    -}
    -
    -func serveHome(ctx iris.Context) {
    -	p, lastMod, err := readFileIfModified(time.Time{})
    -	if err != nil {
    -		p = []byte(err.Error())
    -		lastMod = time.Unix(0, 0)
    -	}
    -	var v = struct {
    -		Host    string
    -		Data    string
    -		LastMod string
    -	}{
    -		ctx.Host(),
    -		string(p),
    -		strconv.FormatInt(lastMod.UnixNano(), 16),
    -	}
    -	ctx.View("home.html", v)
    -}
    -
    -// $ go get github.com/gorilla/websocket
    -// $ go run main.go testfile.txt
    -func main() {
    -	flag.Parse()
    -	if flag.NArg() != 1 {
    -		log.Fatal("filename not specified")
    -	}
    -	filename = flag.Args()[0]
    -
    -	app := iris.New()
    -	app.RegisterView(iris.HTML("./views", ".html"))
    -	app.Get("/", serveHome)
    -	app.Any("/ws", serveWs)
    -
    -	app.Listen(*addr)
    -}
    diff --git a/_examples/websocket/gorilla-filewatch/testfile.txt b/_examples/websocket/gorilla-filewatch/testfile.txt
    deleted file mode 100644
    index 21b4fc73a4..0000000000
    --- a/_examples/websocket/gorilla-filewatch/testfile.txt
    +++ /dev/null
    @@ -1,4 +0,0 @@
    -Some Contents
    -# you can edit this file locally
    -# and http://localhost:8080
    -# will render the new contents through the live websocket connection.
    \ No newline at end of file
    diff --git a/_examples/websocket/gorilla-filewatch/views/home.html b/_examples/websocket/gorilla-filewatch/views/home.html
    deleted file mode 100644
    index e8e1d1d9af..0000000000
    --- a/_examples/websocket/gorilla-filewatch/views/home.html
    +++ /dev/null
    @@ -1,22 +0,0 @@
    -
    -
    -    
    -        WebSocket Example
    -    
    -    
    -        
    {{.Data}}
    - - - \ No newline at end of file diff --git a/_examples/websocket/native-messages/main.go b/_examples/websocket/native-messages/main.go deleted file mode 100644 index 19660642d1..0000000000 --- a/_examples/websocket/native-messages/main.go +++ /dev/null @@ -1,59 +0,0 @@ -package main - -import ( - "log" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/websocket" -) - -type clientPage struct { - Title string - Host string -} - -func main() { - app := iris.New() - - app.RegisterView(iris.HTML("./templates", ".html")) // select the html engine to serve templates - - // Almost all features of neffos are disabled because no custom message can pass - // when app expects to accept and send only raw websocket native messages. - // When only allow native messages is a fact? - // When the registered namespace is just one and it's empty - // and contains only one registered event which is the `OnNativeMessage`. - // When `Events{...}` is used instead of `Namespaces{ "namespaceName": Events{...}}` - // then the namespace is empty "". - ws := websocket.New(websocket.DefaultGorillaUpgrader, websocket.Events{ - websocket.OnNativeMessage: func(nsConn *websocket.NSConn, msg websocket.Message) error { - log.Printf("Server got: %s from [%s]", msg.Body, nsConn.Conn.ID()) - - nsConn.Conn.Server().Broadcast(nsConn, msg) - return nil - }, - }) - - ws.OnConnect = func(c *websocket.Conn) error { - log.Printf("[%s] Connected to server!", c.ID()) - return nil - } - - ws.OnDisconnect = func(c *websocket.Conn) { - log.Printf("[%s] Disconnected from server", c.ID()) - } - - app.HandleDir("/js", iris.Dir("./static/js")) // serve our custom javascript code. - - // register the server on an endpoint. - // see the inline javascript code i the websockets.html, this endpoint is used to connect to the server. - app.Get("/my_endpoint", websocket.Handler(ws)) - - app.Get("/", func(ctx iris.Context) { - ctx.View("client.html", clientPage{"Client Page", "localhost:8080"}) - }) - - // Target some browser windows/tabs to http://localhost:8080 and send some messages, - // see the static/js/chat.js, - // note that the client is using only the browser's native WebSocket API instead of the neffos one. - app.Listen(":8080") -} diff --git a/_examples/websocket/native-messages/static/js/chat.js b/_examples/websocket/native-messages/static/js/chat.js deleted file mode 100644 index 2fe0f7796f..0000000000 --- a/_examples/websocket/native-messages/static/js/chat.js +++ /dev/null @@ -1,35 +0,0 @@ -var messageTxt = document.getElementById("messageTxt"); -var messages = document.getElementById("messages"); -var sendBtn = document.getElementById("sendBtn") - -w = new WebSocket("ws://" + HOST + "/my_endpoint"); -w.onopen = function () { - console.log("Websocket connection enstablished"); -}; - -w.onclose = function () { - appendMessage("

    Disconnected

    "); -}; -w.onmessage = function (message) { - appendMessage("
    " + message.data + "
    "); -}; - -sendBtn.onclick = function () { - myText = messageTxt.value; - messageTxt.value = ""; - - appendMessage("
    me: " + myText + "
    "); - w.send(myText); -}; - -messageTxt.addEventListener("keyup", function (e) { - if (e.keyCode === 13) { - e.preventDefault(); - - sendBtn.click(); - } -}); - -function appendMessage(messageDivHTML) { - messages.insertAdjacentHTML('afterbegin', messageDivHTML); -} diff --git a/_examples/websocket/native-messages/templates/client.html b/_examples/websocket/native-messages/templates/client.html deleted file mode 100644 index 2d8187a265..0000000000 --- a/_examples/websocket/native-messages/templates/client.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - {{ .Title}} - - - - - -
    -
    - - - - - - \ No newline at end of file diff --git a/_examples/websocket/online-visitors/main.go b/_examples/websocket/online-visitors/main.go deleted file mode 100644 index 5df905b4f3..0000000000 --- a/_examples/websocket/online-visitors/main.go +++ /dev/null @@ -1,173 +0,0 @@ -package main - -import ( - "fmt" - "sync/atomic" - - "github.com/kataras/iris/v12" - - "github.com/kataras/iris/v12/websocket" -) - -var events = websocket.Namespaces{ - "default": websocket.Events{ - websocket.OnRoomJoined: onRoomJoined, - websocket.OnRoomLeft: onRoomLeft, - }, -} - -func main() { - // init the web application instance - // app := iris.New() - app := iris.Default() - - // load templates - app.RegisterView(iris.HTML("./templates", ".html").Reload(true)) - // setup the websocket server - ws := websocket.New(websocket.DefaultGorillaUpgrader, events) - - app.Get("/my_endpoint", websocket.Handler(ws)) - - // register static assets request path and system directory - app.HandleDir("/js", iris.Dir("./static/assets/js")) - - h := func(ctx iris.Context) { - ctx.ViewData("", page{PageID: "index page"}) - ctx.View("index.html") - } - - h2 := func(ctx iris.Context) { - ctx.ViewData("", page{PageID: "other page"}) - ctx.View("other.html") - } - - // Open some browser tabs/or windows - // and navigate to - // http://localhost:8080/ and http://localhost:8080/other multiple times. - // Each page has its own online-visitors counter. - app.Get("/", h) - app.Get("/other", h2) - app.Listen(":8080") -} - -type page struct { - PageID string -} - -type pageView struct { - source string - count uint64 -} - -func (v *pageView) increment() { - atomic.AddUint64(&v.count, 1) -} - -func (v *pageView) decrement() { - atomic.AddUint64(&v.count, ^uint64(0)) -} - -func (v *pageView) getCount() uint64 { - return atomic.LoadUint64(&v.count) -} - -type ( - pageViews []pageView -) - -func (v *pageViews) Add(source string) { - args := *v - n := len(args) - for i := 0; i < n; i++ { - kv := &args[i] - if kv.source == source { - kv.increment() - return - } - } - - c := cap(args) - if c > n { - args = args[:n+1] - kv := &args[n] - kv.source = source - kv.count = 1 - *v = args - return - } - - kv := pageView{} - kv.source = source - kv.count = 1 - *v = append(args, kv) -} - -func (v *pageViews) Get(source string) *pageView { - args := *v - n := len(args) - for i := 0; i < n; i++ { - kv := &args[i] - if kv.source == source { - return kv - } - } - return nil -} - -func (v *pageViews) Reset() { - *v = (*v)[:0] -} - -var v pageViews - -func viewsCountBytes(viewsCount uint64) []byte { - // * there are other methods to convert uint64 to []byte - return []byte(fmt.Sprintf("%d", viewsCount)) -} - -func onRoomJoined(ns *websocket.NSConn, msg websocket.Message) error { - // the roomName here is the source. - pageSource := string(msg.Room) - - v.Add(pageSource) - - viewsCount := v.Get(pageSource).getCount() - if viewsCount == 0 { - viewsCount++ // count should be always > 0 here - } - - // fire the "onNewVisit" client event - // on each connection joined to this room (source page) - // and notify of the new visit, - // including this connection (see nil on first input arg). - ns.Conn.Server().Broadcast(nil, websocket.Message{ - Namespace: msg.Namespace, - Room: pageSource, - Event: "onNewVisit", // fire the "onNewVisit" client event. - Body: viewsCountBytes(viewsCount), - }) - - return nil -} - -func onRoomLeft(ns *websocket.NSConn, msg websocket.Message) error { - // the roomName here is the source. - pageV := v.Get(msg.Room) - if pageV == nil { - return nil // for any case that this room is not a pageView source - } - // decrement -1 the specific counter for this page source. - pageV.decrement() - - // fire the "onNewVisit" client event - // on each connection joined to this room (source page) - // and notify of the new, decremented by one, visits count. - ns.Conn.Server().Broadcast(nil, websocket.Message{ - Namespace: msg.Namespace, - Room: msg.Room, - Event: "onNewVisit", - Body: viewsCountBytes(pageV.getCount()), - }) - - return nil -} diff --git a/_examples/websocket/online-visitors/static/assets/js/visitors.js b/_examples/websocket/online-visitors/static/assets/js/visitors.js deleted file mode 100644 index 0e74bc29fb..0000000000 --- a/_examples/websocket/online-visitors/static/assets/js/visitors.js +++ /dev/null @@ -1,24 +0,0 @@ -(function () { - var events = { - default: { - _OnNamespaceConnected: function (ns, msg) { - ns.joinRoom(PAGE_SOURCE); - }, - _OnNamespaceDisconnect: function (ns, msg) { - document.getElementById("online_views").innerHTML = "you've been disconnected"; - }, - onNewVisit: function (ns, msg) { - var text = "1 online view"; - var onlineViews = Number(msg.Body); - if (onlineViews > 1) { - text = onlineViews + " online views"; - } - document.getElementById("online_views").innerHTML = text; - } - } - }; - - neffos.dial("ws://localhost:8080/my_endpoint", events).then(function (client) { - client.connect("default"); - }); -})(); diff --git a/_examples/websocket/online-visitors/templates/index.html b/_examples/websocket/online-visitors/templates/index.html deleted file mode 100644 index 02dd3a92dc..0000000000 --- a/_examples/websocket/online-visitors/templates/index.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - Online visitors example - - - - -
    - 1 online view -
    - - - - - - - - - - \ No newline at end of file diff --git a/_examples/websocket/online-visitors/templates/other.html b/_examples/websocket/online-visitors/templates/other.html deleted file mode 100644 index 11b10d0081..0000000000 --- a/_examples/websocket/online-visitors/templates/other.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - Different page, different results - - - - - - 1 online view - - - - - - - - - - - \ No newline at end of file diff --git a/_examples/websocket/secure/README.md b/_examples/websocket/secure/README.md deleted file mode 100644 index 9b99bafda9..0000000000 --- a/_examples/websocket/secure/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Secure Websockets - -1. Run your server through `https://` (`iris.Run(iris.TLS)` or `iris.Run(iris.AutoTLS)` or a custom `iris.Listener(...)`) -2. Nothing changes inside the whole app, including the websocket side -3. The clients must dial the websocket server endpoint (i.e `/echo`) via `wss://` prefix (instead of the non-secure `ws://`), for example `wss://example.com/echo` -4. Ready to GO. diff --git a/_examples/websocket/socketio/asset/index.html b/_examples/websocket/socketio/asset/index.html deleted file mode 100644 index 6c839ab8e1..0000000000 --- a/_examples/websocket/socketio/asset/index.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - - Socket.IO chat - - - - -
      -
      - -
      - - - - - - \ No newline at end of file diff --git a/_examples/websocket/socketio/go.mod b/_examples/websocket/socketio/go.mod deleted file mode 100644 index 61b7561a1d..0000000000 --- a/_examples/websocket/socketio/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/kataras/iris/_examples/websocket/socketio - -go 1.15 - -require ( - github.com/googollee/go-socket.io v1.4.3-0.20191109153049-7451e2f8c2e0 - github.com/kataras/iris/v12 v12.1.9-0.20200812051831-0edf0affb0bd -) diff --git a/_examples/websocket/socketio/main.go b/_examples/websocket/socketio/main.go deleted file mode 100644 index 9eefe0b366..0000000000 --- a/_examples/websocket/socketio/main.go +++ /dev/null @@ -1,57 +0,0 @@ -// Package main runs a go-socket.io based websocket server. -// An Iris compatible clone of: https://github.com/googollee/go-socket.io#example, -// use of `iris.FromStd` to convert its handler. -package main - -import ( - "fmt" - "log" - - socketio "github.com/googollee/go-socket.io" - "github.com/kataras/iris/v12" -) - -func main() { - app := iris.New() - server, err := socketio.NewServer(nil) - if err != nil { - log.Fatal(err) - } - server.OnConnect("/", func(s socketio.Conn) error { - s.SetContext("") - fmt.Println("connected:", s.ID()) - return nil - }) - server.OnEvent("/", "notice", func(s socketio.Conn, msg string) { - fmt.Println("notice:", msg) - s.Emit("reply", "have "+msg) - }) - server.OnEvent("/chat", "msg", func(s socketio.Conn, msg string) string { - s.SetContext(msg) - return "recv " + msg - }) - server.OnEvent("/", "bye", func(s socketio.Conn) string { - last := s.Context().(string) - s.Emit("bye", last) - s.Close() - return last - }) - server.OnError("/", func(s socketio.Conn, e error) { - fmt.Println("meet error:", e) - }) - server.OnDisconnect("/", func(s socketio.Conn, reason string) { - fmt.Println("closed", reason) - }) - go server.Serve() - defer server.Close() - - app.HandleMany("GET POST", "/socket.io/{any:path}", iris.FromStd(server)) - app.HandleDir("/", iris.Dir("./asset")) - - app.Listen(":8000", iris.WithoutPathCorrection) -} - -/* -If you want to enable CORS in your websocket handler, -please follow this post: https://github.com/googollee/go-socket.io/issues/242 -*/ diff --git a/aliases.go b/aliases.go index b2109572f7..09a65f09f0 100644 --- a/aliases.go +++ b/aliases.go @@ -5,13 +5,13 @@ import ( "path" "regexp" - "github.com/kataras/iris/v12/cache" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/handlerconv" - "github.com/kataras/iris/v12/core/host" - "github.com/kataras/iris/v12/core/router" - "github.com/kataras/iris/v12/hero" - "github.com/kataras/iris/v12/view" + "github.com/kataras/iris/cache" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/handlerconv" + "github.com/kataras/iris/core/host" + "github.com/kataras/iris/core/router" + "github.com/kataras/iris/hero" + "github.com/kataras/iris/view" ) type ( @@ -354,7 +354,7 @@ var ( // Cache304 sends a `StatusNotModified` (304) whenever // the "If-Modified-Since" request header (time) is before the // time.Now() + expiresEvery (always compared to their UTC values). - // Use this, which is a shortcut of the, `chache#Cache304` instead of the "github.com/kataras/iris/v12/cache" or iris.Cache + // Use this, which is a shortcut of the, `chache#Cache304` instead of the "github.com/kataras/iris/cache" or iris.Cache // for better performance. // Clients that are compatible with the http RCF (all browsers are and tools like postman) // will handle the caching. diff --git a/cache/browser.go b/cache/browser.go index 9462cd5f8d..ed8c02f98d 100644 --- a/cache/browser.go +++ b/cache/browser.go @@ -4,8 +4,8 @@ import ( "strconv" "time" - "github.com/kataras/iris/v12/cache/client" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/cache/client" + "github.com/kataras/iris/context" ) // CacheControlHeaderValue is the header value of the @@ -111,7 +111,7 @@ var ETag = func(ctx *context.Context) { // Cache304 sends a `StatusNotModified` (304) whenever // the "If-Modified-Since" request header (time) is before the // time.Now() + expiresEvery (always compared to their UTC values). -// Use this `cache#Cache304` instead of the "github.com/kataras/iris/v12/cache" or iris.Cache +// Use this `cache#Cache304` instead of the "github.com/kataras/iris/cache" or iris.Cache // for better performance. // Clients that are compatible with the http RCF (all browsers are and tools like postman) // will handle the caching. diff --git a/cache/browser_test.go b/cache/browser_test.go deleted file mode 100644 index 7b0eac2e81..0000000000 --- a/cache/browser_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package cache_test - -import ( - "strconv" - "testing" - "time" - - "github.com/kataras/iris/v12/cache" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/httptest" -) - -func TestNoCache(t *testing.T) { - app := iris.New() - app.Get("/", cache.NoCache, func(ctx iris.Context) { - ctx.WriteString("no_cache") - }) - - // tests - e := httptest.New(t, app) - - r := e.GET("/").Expect().Status(httptest.StatusOK) - r.Body().Equal("no_cache") - r.Header(context.CacheControlHeaderKey).Equal(cache.CacheControlHeaderValue) - r.Header(cache.PragmaHeaderKey).Equal(cache.PragmaNoCacheHeaderValue) - r.Header(cache.ExpiresHeaderKey).Equal(cache.ExpiresNeverHeaderValue) -} - -func TestStaticCache(t *testing.T) { - // test change the time format, which is not recommended but can be done. - app := iris.New().Configure(iris.WithTimeFormat("02 Jan 2006 15:04:05 GMT")) - - cacheDur := 30 * (24 * time.Hour) - var expectedTime time.Time - app.Get("/", cache.StaticCache(cacheDur), func(ctx iris.Context) { - expectedTime = time.Now() - ctx.WriteString("static_cache") - }) - - // tests - e := httptest.New(t, app) - r := e.GET("/").Expect().Status(httptest.StatusOK) - r.Body().Equal("static_cache") - - r.Header(cache.ExpiresHeaderKey).Equal(expectedTime.Add(cacheDur).Format(app.ConfigurationReadOnly().GetTimeFormat())) - cacheControlHeaderValue := "public, max-age=" + strconv.Itoa(int(cacheDur.Seconds())) - r.Header(context.CacheControlHeaderKey).Equal(cacheControlHeaderValue) -} - -func TestCache304(t *testing.T) { - // t.Parallel() - app := iris.New() - - expiresEvery := 4 * time.Second - app.Get("/", cache.Cache304(expiresEvery), func(ctx iris.Context) { - ctx.WriteString("send") - }) - // handlers - e := httptest.New(t, app) - - // when 304, content type, content length and if ETagg is there are removed from the headers. - insideCacheTimef := time.Now().Add(-expiresEvery).UTC().Format(app.ConfigurationReadOnly().GetTimeFormat()) - r := e.GET("/").WithHeader(context.IfModifiedSinceHeaderKey, insideCacheTimef).Expect().Status(httptest.StatusNotModified) - r.Headers().NotContainsKey(context.ContentTypeHeaderKey).NotContainsKey(context.ContentLengthHeaderKey).NotContainsKey("ETag") - r.Body().Equal("") - - // continue to the handler itself. - cacheInvalidatedTimef := time.Now().Add(expiresEvery).UTC().Format(app.ConfigurationReadOnly().GetTimeFormat()) // after ~5seconds. - r = e.GET("/").WithHeader(context.LastModifiedHeaderKey, cacheInvalidatedTimef).Expect().Status(httptest.StatusOK) - r.Body().Equal("send") - // now without header, it should continue to the handler itself as well. - r = e.GET("/").Expect().Status(httptest.StatusOK) - r.Body().Equal("send") -} - -func TestETag(t *testing.T) { - // t.Parallel() - - app := iris.New() - n := "_" - app.Get("/", cache.ETag, func(ctx iris.Context) { - ctx.WriteString(n) - n += "_" - }) - - // the first and last test writes the content with status OK without cache, - // the rest tests the cache headers and status 304 and return, so body should be "". - e := httptest.New(t, app) - - r := e.GET("/").Expect().Status(httptest.StatusOK) - r.Header("ETag").Equal("/") // test if header set. - r.Body().Equal("_") - - e.GET("/").WithHeader("ETag", "/").WithHeader("If-None-Match", "/").Expect(). - Status(httptest.StatusNotModified).Body().Equal("") // browser is responsible, no the test engine. - - r = e.GET("/").Expect().Status(httptest.StatusOK) - r.Header("ETag").Equal("/") // test if header set. - r.Body().Equal("__") -} diff --git a/cache/cache.go b/cache/cache.go index 9babe5c101..b02d823884 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -9,8 +9,8 @@ Example code: import ( "time" - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/cache" + "github.com/kataras/iris" + "github.com/kataras/iris/cache" ) func main(){ @@ -30,8 +30,8 @@ package cache import ( "time" - "github.com/kataras/iris/v12/cache/client" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/cache/client" + "github.com/kataras/iris/context" ) // Cache accepts the cache expiration duration. diff --git a/cache/cache_test.go b/cache/cache_test.go deleted file mode 100644 index f80f15b3dc..0000000000 --- a/cache/cache_test.go +++ /dev/null @@ -1,215 +0,0 @@ -package cache_test - -import ( - "fmt" - "net/http" - "sync/atomic" - "testing" - "time" - - "github.com/kataras/iris/v12/cache" - "github.com/kataras/iris/v12/cache/client" - "github.com/kataras/iris/v12/cache/client/rule" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - - "github.com/iris-contrib/httpexpect/v2" - "github.com/kataras/iris/v12/httptest" -) - -var ( - cacheDuration = 2 * time.Second - expectedBodyStr = "Imagine it as a big message to achieve x20 response performance!" -) - -type testError struct { - expected int - got uint32 -} - -func (h *testError) Error() string { - return fmt.Sprintf("expected the main handler to be executed %d times instead of %d", h.expected, h.got) -} - -func runTest(e *httpexpect.Expect, path string, counterPtr *uint32, expectedBodyStr string, nocache string) error { - e.GET(path).Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) - time.Sleep(cacheDuration / 5) // lets wait for a while, cache should be saved and ready - e.GET(path).Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) - counter := atomic.LoadUint32(counterPtr) - if counter > 1 { - // n should be 1 because it doesn't changed after the first call - return &testError{1, counter} - } - time.Sleep(cacheDuration) - - // cache should be cleared now - e.GET(path).Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) - time.Sleep(cacheDuration / 5) - // let's call again , the cache should be saved - e.GET(path).Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) - counter = atomic.LoadUint32(counterPtr) - if counter != 2 { - return &testError{2, counter} - } - - // we have cache response saved for the path, we have some time more here, but here - // we will make the requestS with some of the deniers options - e.GET(path).WithHeader("max-age", "0").Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) - e.GET(path).WithHeader("Authorization", "basic or anything").Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) - counter = atomic.LoadUint32(counterPtr) - if counter != 4 { - return &testError{4, counter} - } - - if nocache != "" { - // test the NoCache, first sleep to pass the cache expiration, - // second add to the cache with a valid request and response - // third, do it with the "/nocache" path (static for now, pure test design) given by the consumer - time.Sleep(cacheDuration) - - // cache should be cleared now, this should work because we are not in the "nocache" path - e.GET("/").Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) // counter = 5 - time.Sleep(cacheDuration / 5) - - // let's call the "nocache", the expiration is not passed so but the "nocache" - // route's path has the cache.NoCache so it should be not cached and the counter should be ++ - e.GET(nocache).Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) // counter should be 6 - counter = atomic.LoadUint32(counterPtr) - if counter != 6 { // 4 before, 5 with the first call to store the cache, and six with the no cache, again original handler executation - return &testError{6, counter} - } - - // let's call again the path the expiration is not passed so it should be cached - e.GET(path).Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) - counter = atomic.LoadUint32(counterPtr) - if counter != 6 { - return &testError{6, counter} - } - - // but now check for the No - } - - return nil -} - -func TestClientNoCache(t *testing.T) { - app := iris.New() - var n uint32 - - app.Get("/", cache.Handler(cacheDuration), func(ctx *context.Context) { - atomic.AddUint32(&n, 1) - ctx.Write([]byte(expectedBodyStr)) - }) - - app.Get("/nocache", cache.Handler(cacheDuration), func(ctx *context.Context) { - client.NoCache(ctx) // <---- - atomic.AddUint32(&n, 1) - ctx.Write([]byte(expectedBodyStr)) - }) - - e := httptest.New(t, app) - if err := runTest(e, "/", &n, expectedBodyStr, "/nocache"); err != nil { - t.Fatalf(t.Name()+": %v", err) - } -} - -func TestCache(t *testing.T) { - app := iris.New() - var n uint32 - - app.Use(cache.Handler(cacheDuration)) - - app.Get("/", func(ctx *context.Context) { - atomic.AddUint32(&n, 1) - ctx.Write([]byte(expectedBodyStr)) - }) - - var ( - n2 uint32 - expectedBodyStr2 = "This is the other" - ) - - app.Get("/other", func(ctx *context.Context) { - atomic.AddUint32(&n2, 1) - ctx.Write([]byte(expectedBodyStr2)) - }) - - e := httptest.New(t, app) - if err := runTest(e, "/", &n, expectedBodyStr, ""); err != nil { - t.Fatalf(t.Name()+": %v", err) - } - - if err := runTest(e, "/other", &n2, expectedBodyStr2, ""); err != nil { - t.Fatalf(t.Name()+" other: %v", err) - } -} - -// This works but we have issue on golog.SetLevel and get golog.Level on httptest.New -// when tests are running in parallel and the loggers are used. -// // TODO: Fix it on golog repository or here, we'll see. -// func TestCacheHandlerParallel(t *testing.T) { -// t.Parallel() -// TestCache(t) -// } - -func TestCacheValidator(t *testing.T) { - app := iris.New() - var n uint32 - - h := func(ctx *context.Context) { - atomic.AddUint32(&n, 1) - ctx.Write([]byte(expectedBodyStr)) - } - - validCache := cache.Cache(cacheDuration) - app.Get("/", validCache.ServeHTTP, h) - - managedCache := cache.Cache(cacheDuration) - managedCache.AddRule(rule.Validator([]rule.PreValidator{ - func(ctx *context.Context) bool { - // should always invalid for cache, don't bother to go to try to get or set cache - return ctx.Request().URL.Path != "/invalid" - }, - }, nil)) - - managedCache2 := cache.Cache(cacheDuration) - managedCache2.AddRule(rule.Validator(nil, - []rule.PostValidator{ - func(ctx *context.Context) bool { - // it's passed the Claim and now Valid checks if the response contains a header of "DONT" - return ctx.ResponseWriter().Header().Get("DONT") == "" - }, - }, - )) - - app.Get("/valid", validCache.ServeHTTP, h) - - app.Get("/invalid", managedCache.ServeHTTP, h) - app.Get("/invalid2", managedCache2.ServeHTTP, func(ctx *context.Context) { - atomic.AddUint32(&n, 1) - ctx.Header("DONT", "DO not cache that response even if it was claimed") - ctx.Write([]byte(expectedBodyStr)) - }) - - e := httptest.New(t, app) - - // execute from cache the next time - e.GET("/valid").Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) - time.Sleep(cacheDuration / 5) // lets wait for a while, cache should be saved and ready - e.GET("/valid").Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) - counter := atomic.LoadUint32(&n) - if counter > 1 { - // n should be 1 because it doesn't changed after the first call - t.Fatalf("%s: %v", t.Name(), &testError{1, counter}) - } - // don't execute from cache, execute the original, counter should ++ here - e.GET("/invalid").Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) // counter = 2 - e.GET("/invalid2").Expect().Status(http.StatusOK).Body().Equal(expectedBodyStr) // counter = 3 - - counter = atomic.LoadUint32(&n) - if counter != 3 { - // n should be 1 because it doesn't changed after the first call - t.Fatalf("%s: %v", t.Name(), &testError{3, counter}) - } -} diff --git a/cache/client/client.go b/cache/client/client.go index 5d89c42955..c941131ff1 100644 --- a/cache/client/client.go +++ b/cache/client/client.go @@ -6,10 +6,10 @@ import ( "net/http" "time" - "github.com/kataras/iris/v12/cache/cfg" - "github.com/kataras/iris/v12/cache/client/rule" - "github.com/kataras/iris/v12/cache/uri" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/cache/cfg" + "github.com/kataras/iris/cache/client/rule" + "github.com/kataras/iris/cache/uri" + "github.com/kataras/iris/context" ) // ClientHandler is the client-side handler diff --git a/cache/client/handler.go b/cache/client/handler.go index ffeae084ca..4e4b73a135 100644 --- a/cache/client/handler.go +++ b/cache/client/handler.go @@ -4,9 +4,9 @@ import ( "sync" "time" - "github.com/kataras/iris/v12/cache/client/rule" - "github.com/kataras/iris/v12/cache/entry" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/cache/client/rule" + "github.com/kataras/iris/cache/entry" + "github.com/kataras/iris/context" ) func init() { diff --git a/cache/client/rule/chained.go b/cache/client/rule/chained.go index 20a29960d0..6377830939 100644 --- a/cache/client/rule/chained.go +++ b/cache/client/rule/chained.go @@ -1,7 +1,7 @@ package rule import ( - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) // chainedRule is a Rule with next Rule diff --git a/cache/client/rule/conditional.go b/cache/client/rule/conditional.go index a07f0cd3f9..82e12e96c1 100644 --- a/cache/client/rule/conditional.go +++ b/cache/client/rule/conditional.go @@ -1,7 +1,7 @@ package rule import ( - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) // Conditional is a Rule witch adds a predicate in order to its methods to execute diff --git a/cache/client/rule/header.go b/cache/client/rule/header.go index b118fec477..c99728945c 100644 --- a/cache/client/rule/header.go +++ b/cache/client/rule/header.go @@ -1,9 +1,9 @@ package rule import ( - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" - "github.com/kataras/iris/v12/cache/ruleset" + "github.com/kataras/iris/cache/ruleset" ) // The HeaderPredicate should be alived on each of $package/rule BUT GOLANG DOESN'T SUPPORT type alias and I don't want to have so many copies around diff --git a/cache/client/rule/not_satisfied.go b/cache/client/rule/not_satisfied.go index cfd85f9ae3..bab15d8050 100644 --- a/cache/client/rule/not_satisfied.go +++ b/cache/client/rule/not_satisfied.go @@ -1,7 +1,7 @@ package rule import ( - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) type notSatisfiedRule struct{} diff --git a/cache/client/rule/rule.go b/cache/client/rule/rule.go index 92fe252ff4..0c85dd18a7 100644 --- a/cache/client/rule/rule.go +++ b/cache/client/rule/rule.go @@ -1,6 +1,6 @@ package rule -import "github.com/kataras/iris/v12/context" +import "github.com/kataras/iris/context" // Rule a superset of validators type Rule interface { diff --git a/cache/client/rule/satisfied.go b/cache/client/rule/satisfied.go index 9fd40875f0..cf342fa5f0 100644 --- a/cache/client/rule/satisfied.go +++ b/cache/client/rule/satisfied.go @@ -1,7 +1,7 @@ package rule import ( - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) type satisfiedRule struct{} diff --git a/cache/client/rule/validator.go b/cache/client/rule/validator.go index 1f8ca36c8c..af38f5949f 100644 --- a/cache/client/rule/validator.go +++ b/cache/client/rule/validator.go @@ -1,6 +1,6 @@ package rule -import "github.com/kataras/iris/v12/context" +import "github.com/kataras/iris/context" // Validators are introduced to implement the RFC about cache (https://tools.ietf.org/html/rfc7234#section-1.1). diff --git a/cache/client/ruleset.go b/cache/client/ruleset.go index 52ec35a1c0..7ce414c13b 100644 --- a/cache/client/ruleset.go +++ b/cache/client/ruleset.go @@ -1,10 +1,10 @@ package client import ( - "github.com/kataras/iris/v12/cache/cfg" - "github.com/kataras/iris/v12/cache/client/rule" - "github.com/kataras/iris/v12/cache/ruleset" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/cache/cfg" + "github.com/kataras/iris/cache/client/rule" + "github.com/kataras/iris/cache/ruleset" + "github.com/kataras/iris/context" ) // DefaultRuleSet is a list of the default pre-cache validators diff --git a/cache/entry/entry.go b/cache/entry/entry.go index f03b8342aa..ed79ce64fd 100644 --- a/cache/entry/entry.go +++ b/cache/entry/entry.go @@ -3,7 +3,7 @@ package entry import ( "time" - "github.com/kataras/iris/v12/cache/cfg" + "github.com/kataras/iris/cache/cfg" ) // Entry is the cache entry diff --git a/cache/uri/uribuilder.go b/cache/uri/uribuilder.go index 3ee078c79d..f14da064a7 100644 --- a/cache/uri/uribuilder.go +++ b/cache/uri/uribuilder.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "github.com/kataras/iris/v12/cache/cfg" + "github.com/kataras/iris/cache/cfg" ) // URIBuilder is the requested url builder diff --git a/cli.go b/cli.go index bed303b09c..c90ad63e55 100644 --- a/cli.go +++ b/cli.go @@ -13,8 +13,8 @@ import ( "path/filepath" "strings" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/router" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/router" "gopkg.in/yaml.v3" ) diff --git a/configuration.go b/configuration.go index e9dcf3aba0..e1518b9e52 100644 --- a/configuration.go +++ b/configuration.go @@ -11,8 +11,8 @@ import ( "strings" "github.com/kataras/golog" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/netutil" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/netutil" "github.com/BurntSushi/toml" "github.com/kataras/sitemap" diff --git a/configuration_test.go b/configuration_test.go deleted file mode 100644 index ae0e3edfc8..0000000000 --- a/configuration_test.go +++ /dev/null @@ -1,360 +0,0 @@ -package iris - -import ( - "io/ioutil" - "os" - "reflect" - "testing" - "time" - - "gopkg.in/yaml.v3" -) - -// $ go test -v -run TestConfiguration* - -func TestConfigurationStatic(t *testing.T) { - def := DefaultConfiguration() - - app := New() - afterNew := *app.config - - if !reflect.DeepEqual(def, afterNew) { - t.Fatalf("Default configuration is not the same after NewFromConfig expected:\n %#v \ngot:\n %#v", def, afterNew) - } - - afterNew.Charset = "changed" - - if reflect.DeepEqual(def, afterNew) { - t.Fatalf("Configuration should be not equal, got: %#v", afterNew) - } - - app = New().Configure(WithConfiguration(Configuration{DisableBodyConsumptionOnUnmarshal: true})) - - afterNew = *app.config - - if !app.config.DisableBodyConsumptionOnUnmarshal { - t.Fatalf("Passing a Configuration field as Option fails, expected DisableBodyConsumptionOnUnmarshal to be true but was false") - } - - app = New() // empty , means defaults so - if !reflect.DeepEqual(def, *app.config) { - t.Fatalf("Default configuration is not the same after NewFromConfig expected:\n %#v \ngot:\n %#v", def, *app.config) - } -} - -func TestConfigurationOptions(t *testing.T) { - charset := "MYCHARSET" - disableBodyConsumptionOnUnmarshal := true - disableBanner := true - - app := New().Configure(WithCharset(charset), WithoutBodyConsumptionOnUnmarshal, WithoutBanner) - - if got := app.config.Charset; got != charset { - t.Fatalf("Expected configuration Charset to be: %s but got: %s", charset, got) - } - - if got := app.config.DisableBodyConsumptionOnUnmarshal; got != disableBodyConsumptionOnUnmarshal { - t.Fatalf("Expected configuration DisableBodyConsumptionOnUnmarshal to be: %#v but got: %#v", disableBodyConsumptionOnUnmarshal, got) - } - - if got := app.config.DisableStartupLog; got != disableBanner { - t.Fatalf("Expected configuration DisableStartupLog to be: %#v but got: %#v", disableBanner, got) - } - - // now check if other default values are set (should be set automatically) - - expected := DefaultConfiguration() - expected.Charset = charset - expected.DisableBodyConsumptionOnUnmarshal = disableBodyConsumptionOnUnmarshal - expected.DisableStartupLog = disableBanner - - has := *app.config - if !reflect.DeepEqual(has, expected) { - t.Fatalf("Default configuration is not the same after New expected:\n %#v \ngot:\n %#v", expected, has) - } -} - -func TestConfigurationOptionsDeep(t *testing.T) { - charset := "MYCHARSET" - - app := New().Configure(WithCharset(charset), WithoutBodyConsumptionOnUnmarshal, WithoutBanner) - - expected := DefaultConfiguration() - expected.Charset = charset - expected.DisableBodyConsumptionOnUnmarshal = true - expected.DisableStartupLog = true - - has := *app.config - - if !reflect.DeepEqual(has, expected) { - t.Fatalf("DEEP configuration is not the same after New expected:\n %#v \ngot:\n %#v", expected, has) - } -} - -func createGlobalConfiguration(t *testing.T) { - filename := homeConfigurationFilename(".yml") - c, err := parseYAML(filename) - if err != nil { - // this error will be occurred the first time that the configuration - // file doesn't exist. - // Create the YAML-ONLY global configuration file now using the default configuration 'c'. - // This is useful when we run multiple iris servers that share the same - // configuration, even with custom values at its "Other" field. - out, err := yaml.Marshal(&c) - - if err == nil { - err = ioutil.WriteFile(filename, out, os.FileMode(0666)) - } - if err != nil { - t.Fatalf("error while writing the global configuration field at: %s. Trace: %v\n", filename, err) - } - } -} - -func TestConfigurationGlobal(t *testing.T) { - t.Cleanup(func() { - os.Remove(homeConfigurationFilename(".yml")) - }) - - createGlobalConfiguration(t) - - testConfigurationGlobal(t, WithGlobalConfiguration) - testConfigurationGlobal(t, WithConfiguration(YAML(globalConfigurationKeyword))) -} - -func testConfigurationGlobal(t *testing.T, c Configurator) { - app := New().Configure(c) - - if has, expected := *app.config, DefaultConfiguration(); !reflect.DeepEqual(has, expected) { - t.Fatalf("global configuration (which should be defaulted) is not the same with the default one:\n %#v \ngot:\n %#v", has, expected) - } -} - -func TestConfigurationYAML(t *testing.T) { - yamlFile, ferr := ioutil.TempFile("", "configuration.yml") - - if ferr != nil { - t.Fatal(ferr) - } - - defer func() { - yamlFile.Close() - time.Sleep(50 * time.Millisecond) - os.Remove(yamlFile.Name()) - }() - - yamlConfigurationContents := ` -DisablePathCorrection: false -DisablePathCorrectionRedirection: true -EnablePathIntelligence: true -EnablePathEscape: false -FireMethodNotAllowed: true -EnableOptimizations: true -DisableBodyConsumptionOnUnmarshal: true -TimeFormat: "Mon, 02 Jan 2006 15:04:05 GMT" -Charset: "utf-8" -RemoteAddrHeaders: - - X-Real-Ip - - X-Forwarded-For - - CF-Connecting-IP -HostProxyHeaders: - X-Host: true -SSLProxyHeaders: - X-Forwarded-Proto: https -Other: - MyServerName: "Iris: https://github.com/kataras/iris" -` - yamlFile.WriteString(yamlConfigurationContents) - filename := yamlFile.Name() - app := New().Configure(WithConfiguration(YAML(filename))) - - c := app.config - - if expected := false; c.DisablePathCorrection != expected { - t.Fatalf("error on TestConfigurationYAML: Expected DisablePathCorrection %v but got %v", expected, c.DisablePathCorrection) - } - - if expected := true; c.DisablePathCorrectionRedirection != expected { - t.Fatalf("error on TestConfigurationYAML: Expected DisablePathCorrectionRedirection %v but got %v", expected, c.DisablePathCorrectionRedirection) - } - - if expected := true; c.EnablePathIntelligence != expected { - t.Fatalf("error on TestConfigurationYAML: Expected EnablePathIntelligence %v but got %v", expected, c.EnablePathIntelligence) - } - - if expected := false; c.EnablePathEscape != expected { - t.Fatalf("error on TestConfigurationYAML: Expected EnablePathEscape %v but got %v", expected, c.EnablePathEscape) - } - - if expected := true; c.EnableOptimizations != expected { - t.Fatalf("error on TestConfigurationYAML: Expected EnableOptimizations %v but got %v", expected, c.EnablePathEscape) - } - - if expected := true; c.FireMethodNotAllowed != expected { - t.Fatalf("error on TestConfigurationYAML: Expected FireMethodNotAllowed %v but got %v", expected, c.FireMethodNotAllowed) - } - - if expected := true; c.DisableBodyConsumptionOnUnmarshal != expected { - t.Fatalf("error on TestConfigurationYAML: Expected DisableBodyConsumptionOnUnmarshal %v but got %v", expected, c.DisableBodyConsumptionOnUnmarshal) - } - - if expected := "Mon, 02 Jan 2006 15:04:05 GMT"; c.TimeFormat != expected { - t.Fatalf("error on TestConfigurationYAML: Expected TimeFormat %s but got %s", expected, c.TimeFormat) - } - - if expected := "utf-8"; c.Charset != expected { - t.Fatalf("error on TestConfigurationYAML: Expected Charset %s but got %s", expected, c.Charset) - } - - if len(c.RemoteAddrHeaders) == 0 { - t.Fatalf("error on TestConfigurationYAML: Expected RemoteAddrHeaders to be filled") - } - - expectedRemoteAddrHeaders := []string{ - "X-Real-Ip", - "X-Forwarded-For", - "CF-Connecting-IP", - } - - if expected, got := len(c.RemoteAddrHeaders), len(expectedRemoteAddrHeaders); expected != got { - t.Fatalf("error on TestConfigurationYAML: Expected RemoteAddrHeaders' len(%d) and got(%d), len is not the same", expected, got) - } - - for i, v := range c.RemoteAddrHeaders { - if expected, got := expectedRemoteAddrHeaders[i], v; expected != got { - t.Fatalf("error on TestConfigurationYAML: Expected RemoteAddrHeaders[%d] = %s but got %s", i, expected, got) - } - } - - expectedHostProxyHeaders := map[string]bool{ - "X-Host": true, - } - - if expected, got := len(c.HostProxyHeaders), len(expectedHostProxyHeaders); expected != got { - t.Fatalf("error on TestConfigurationYAML: Expected HostProxyHeaders' len(%d) and got(%d), len is not the same", expected, got) - } - - for k, v := range c.HostProxyHeaders { - if expected, got := expectedHostProxyHeaders[k], v; expected != got { - t.Fatalf("error on TestConfigurationYAML: Expected HostProxyHeaders[%s] = %t but got %t", k, expected, got) - } - } - - expectedSSLProxyHeaders := map[string]string{ - "X-Forwarded-Proto": "https", - } - - if expected, got := len(c.SSLProxyHeaders), len(c.SSLProxyHeaders); expected != got { - t.Fatalf("error on TestConfigurationYAML: Expected SSLProxyHeaders' len(%d) and got(%d), len is not the same", expected, got) - } - - for k, v := range c.SSLProxyHeaders { - if expected, got := expectedSSLProxyHeaders[k], v; expected != got { - t.Fatalf("error on TestConfigurationYAML: Expected SSLProxyHeaders[%s] = %s but got %s", k, expected, got) - } - } - - if len(c.Other) == 0 { - t.Fatalf("error on TestConfigurationYAML: Expected Other to be filled") - } - - if expected, got := "Iris: https://github.com/kataras/iris", c.Other["MyServerName"]; expected != got { - t.Fatalf("error on TestConfigurationYAML: Expected Other['MyServerName'] %s but got %s", expected, got) - } -} - -func TestConfigurationTOML(t *testing.T) { - tomlFile, ferr := ioutil.TempFile("", "configuration.toml") - - if ferr != nil { - t.Fatal(ferr) - } - - defer func() { - tomlFile.Close() - time.Sleep(50 * time.Millisecond) - os.Remove(tomlFile.Name()) - }() - - tomlConfigurationContents := ` -DisablePathCorrectionRedirection = true -EnablePathEscape = false -FireMethodNotAllowed = true -EnableOptimizations = true -DisableBodyConsumptionOnUnmarshal = true -TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" -Charset = "utf-8" - -RemoteAddrHeaders = ["X-Real-Ip", "X-Forwarded-For", "CF-Connecting-IP"] - -[Other] - # Indentation (tabs and/or spaces) is allowed but not required - MyServerName = "Iris: https://github.com/kataras/iris" - -` - tomlFile.WriteString(tomlConfigurationContents) - filename := tomlFile.Name() - app := New().Configure(WithConfiguration(TOML(filename))) - - c := app.config - - if expected := false; c.DisablePathCorrection != expected { - t.Fatalf("error on TestConfigurationTOML: Expected DisablePathCorrection %v but got %v", expected, c.DisablePathCorrection) - } - - if expected := true; c.DisablePathCorrectionRedirection != expected { - t.Fatalf("error on TestConfigurationTOML: Expected DisablePathCorrectionRedirection %v but got %v", expected, c.DisablePathCorrectionRedirection) - } - - if expected := false; c.EnablePathEscape != expected { - t.Fatalf("error on TestConfigurationTOML: Expected EnablePathEscape %v but got %v", expected, c.EnablePathEscape) - } - - if expected := true; c.EnableOptimizations != expected { - t.Fatalf("error on TestConfigurationTOML: Expected EnableOptimizations %v but got %v", expected, c.EnablePathEscape) - } - - if expected := true; c.FireMethodNotAllowed != expected { - t.Fatalf("error on TestConfigurationTOML: Expected FireMethodNotAllowed %v but got %v", expected, c.FireMethodNotAllowed) - } - - if expected := true; c.DisableBodyConsumptionOnUnmarshal != expected { - t.Fatalf("error on TestConfigurationTOML: Expected DisableBodyConsumptionOnUnmarshal %v but got %v", expected, c.DisableBodyConsumptionOnUnmarshal) - } - - if expected := "Mon, 02 Jan 2006 15:04:05 GMT"; c.TimeFormat != expected { - t.Fatalf("error on TestConfigurationTOML: Expected TimeFormat %s but got %s", expected, c.TimeFormat) - } - - if expected := "utf-8"; c.Charset != expected { - t.Fatalf("error on TestConfigurationTOML: Expected Charset %s but got %s", expected, c.Charset) - } - - if len(c.RemoteAddrHeaders) == 0 { - t.Fatalf("error on TestConfigurationTOML: Expected RemoteAddrHeaders to be filled") - } - - expectedRemoteAddrHeaders := []string{ - "X-Real-Ip", - "X-Forwarded-For", - "CF-Connecting-IP", - } - - if expected, got := len(c.RemoteAddrHeaders), len(expectedRemoteAddrHeaders); expected != got { - t.Fatalf("error on TestConfigurationTOML: Expected RemoteAddrHeaders' len(%d) and got(%d), len is not the same", expected, got) - } - - for i, got := range c.RemoteAddrHeaders { - if expected := expectedRemoteAddrHeaders[i]; expected != got { - t.Fatalf("error on TestConfigurationTOML: Expected RemoteAddrHeaders[%d] = %s but got %s", i, expected, got) - } - } - - if len(c.Other) == 0 { - t.Fatalf("error on TestConfigurationTOML: Expected Other to be filled") - } - - if expected, got := "Iris: https://github.com/kataras/iris", c.Other["MyServerName"]; expected != got { - t.Fatalf("error on TestConfigurationTOML: Expected Other['MyServerName'] %s but got %s", expected, got) - } -} diff --git a/context/configuration.go b/context/configuration.go index fa9091d7d6..15cc8567dc 100644 --- a/context/configuration.go +++ b/context/configuration.go @@ -1,6 +1,6 @@ package context -import "github.com/kataras/iris/v12/core/netutil" +import "github.com/kataras/iris/core/netutil" // ConfigurationReadOnly can be implemented // by Configuration, it's being used inside the Context. diff --git a/context/context.go b/context/context.go index afd0aee3f1..b7d723acd3 100644 --- a/context/context.go +++ b/context/context.go @@ -25,8 +25,8 @@ import ( "time" "unsafe" - "github.com/kataras/iris/v12/core/memstore" - "github.com/kataras/iris/v12/core/netutil" + "github.com/kataras/iris/core/memstore" + "github.com/kataras/iris/core/netutil" "github.com/Shopify/goreferrer" "github.com/fatih/structs" diff --git a/context/handler.go b/context/handler.go index 6e65353631..a61e0bdf4b 100644 --- a/context/handler.go +++ b/context/handler.go @@ -31,7 +31,7 @@ var ( // If the name starts with "iris" then it replaces that string with the // full Iris module package name, // e.g. iris/middleware/logger.(*requestLoggerMiddleware).ServeHTTP-fm to -// github.com/kataras/iris/v12/middleware/logger.(*requestLoggerMiddleware).ServeHTTP-fm +// github.com/kataras/iris/middleware/logger.(*requestLoggerMiddleware).ServeHTTP-fm // for convenient between Iris versions. func SetHandlerName(original string, replacement string) { if strings.HasPrefix(original, "iris") { diff --git a/context/request_params.go b/context/request_params.go index 4aa4ae7891..ccc48d636e 100644 --- a/context/request_params.go +++ b/context/request_params.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/kataras/iris/v12/core/memstore" + "github.com/kataras/iris/core/memstore" ) // RequestParams is a key string - value string storage which diff --git a/context/route.go b/context/route.go index 56104828e0..6e33229202 100644 --- a/context/route.go +++ b/context/route.go @@ -4,7 +4,7 @@ import ( "io" "time" - "github.com/kataras/iris/v12/macro" + "github.com/kataras/iris/macro" ) // RouteReadOnly allows decoupled access to the current route diff --git a/core/errgroup/errgroup_test.go b/core/errgroup/errgroup_test.go deleted file mode 100644 index f01fbfff02..0000000000 --- a/core/errgroup/errgroup_test.go +++ /dev/null @@ -1,173 +0,0 @@ -package errgroup - -import ( - "errors" - "fmt" - "strings" - "testing" -) - -func TestErrorError(t *testing.T) { - testErr := errors.New("error") - err := &Error{Err: testErr} - if expected, got := testErr.Error(), err.Error(); expected != got { - t.Fatalf("expected %s but got %s", expected, got) - } -} - -func TestErrorUnwrap(t *testing.T) { - wrapped := errors.New("unwrap") - - err := &Error{Err: fmt.Errorf("this wraps:%w", wrapped)} - if expected, got := wrapped, errors.Unwrap(err); expected != got { - t.Fatalf("expected %#+v but got %#+v", expected, got) - } - -} -func TestErrorIs(t *testing.T) { - testErr := errors.New("is") - err := &Error{Err: fmt.Errorf("this is: %w", testErr)} - if expected, got := true, errors.Is(err, testErr); expected != got { - t.Fatalf("expected %v but got %v", expected, got) - } -} - -func TestErrorAs(t *testing.T) { - testErr := errors.New("as") - err := &Error{Err: testErr} - if expected, got := true, errors.As(err, &testErr); expected != got { - t.Fatalf("[testErr as err] expected %v but got %v", expected, got) - } - if expected, got := true, errors.As(testErr, &err); expected != got { - t.Fatalf("[err as testErr] expected %v but got %v", expected, got) - } -} - -func TestGroupError(t *testing.T) { - g := New(0) - tests := []string{"error 1", "error 2", "error 3"} - for _, tt := range tests { - g.Add(errors.New(tt)) - } - - if expected, got := strings.Join(tests, "\n"), g.Error(); expected != got { - t.Fatalf("expected '%s' but got '%s'", expected, got) - } -} - -func TestGroup(t *testing.T) { - const ( - apiErrorsType = iota + 1 - childAPIErrorsType - childAPIErrors2Type = "string type 1" - childAPIErrors2Type1 = "string type 2" - - apiErrorsText = "apiErrors error 1" - childAPIErrorsText = "apiErrors:child error 1" - childAPIErrors2Text = "apiErrors:child2 error 1" - childAPIErrors2Text1 = "apiErrors:child2_1 error 1" - ) - - g := New(nil) - apiErrorsGroup := g.Group(apiErrorsType) - apiErrorsGroup.Errf(apiErrorsText) - - childAPIErrorsGroup := apiErrorsGroup.Group(childAPIErrorsType) - childAPIErrorsGroup.Addf(childAPIErrorsText) - childAPIErrorsGroup2 := apiErrorsGroup.Group(childAPIErrors2Type) - childAPIErrorsGroup2.Addf(childAPIErrors2Text) - childAPIErrorsGroup2Group1 := childAPIErrorsGroup2.Group(childAPIErrors2Type1) - childAPIErrorsGroup2Group1.Addf(childAPIErrors2Text1) - - if apiErrorsGroup.Type != apiErrorsType { - t.Fatal("invalid type") - } - - if childAPIErrorsGroup.Type != childAPIErrorsType { - t.Fatal("invalid type") - } - - if childAPIErrorsGroup2.Type != childAPIErrors2Type { - t.Fatal("invalid type") - } - - if childAPIErrorsGroup2Group1.Type != childAPIErrors2Type1 { - t.Fatal("invalid type") - } - - if expected, got := 2, len(apiErrorsGroup.children); expected != got { - t.Fatalf("expected %d but got %d", expected, got) - } - - if expected, got := 0, len(childAPIErrorsGroup.children); expected != got { - t.Fatalf("expected %d but got %d", expected, got) - } - - if expected, got := 1, len(childAPIErrorsGroup2.children); expected != got { - t.Fatalf("expected %d but got %d", expected, got) - } - - if expected, got := 0, len(childAPIErrorsGroup2Group1.children); expected != got { - t.Fatalf("expected %d but got %d", expected, got) - } - - if expected, got := 1, apiErrorsGroup.index; expected != got { - t.Fatalf("expected index %d but got %d", expected, got) - } - - if expected, got := 2, childAPIErrorsGroup.index; expected != got { - t.Fatalf("expected index %d but got %d", expected, got) - } - - if expected, got := 3, childAPIErrorsGroup2.index; expected != got { - t.Fatalf("expected index %d but got %d", expected, got) - } - - if expected, got := 4, childAPIErrorsGroup2Group1.index; expected != got { - t.Fatalf("expected index %d but got %d", expected, got) - } - - t.Run("Error", func(t *testing.T) { - if expected, got := - strings.Join([]string{apiErrorsText, childAPIErrorsText, childAPIErrors2Text, childAPIErrors2Text1}, delim), g.Error(); expected != got { - t.Fatalf("expected '%s' but got '%s'", expected, got) - } - }) - - t.Run("Walk", func(t *testing.T) { - expectedEntries := 4 - _ = Walk(g, func(typ interface{}, err error) { - g.IncludeChildren = false - childAPIErrorsGroup.IncludeChildren = false - childAPIErrorsGroup2.IncludeChildren = false - childAPIErrorsGroup2Group1.IncludeChildren = false - - expectedEntries-- - var expected string - - switch typ { - case apiErrorsType: - expected = apiErrorsText - case childAPIErrorsType: - expected = childAPIErrorsText - case childAPIErrors2Type: - expected = childAPIErrors2Text - case childAPIErrors2Type1: - expected = childAPIErrors2Text1 - } - - if got := err.Error(); expected != got { - t.Fatalf("[%v] expected '%s' but got '%s'", typ, expected, got) - } - }) - - if expectedEntries != 0 { - t.Fatalf("not valid number of errors [...%d]", expectedEntries) - } - - g.IncludeChildren = true - childAPIErrorsGroup.IncludeChildren = true - childAPIErrorsGroup2.IncludeChildren = true - childAPIErrorsGroup2Group1.IncludeChildren = true - }) -} diff --git a/core/handlerconv/from_std.go b/core/handlerconv/from_std.go index d42ac1c000..983ddd43d1 100644 --- a/core/handlerconv/from_std.go +++ b/core/handlerconv/from_std.go @@ -4,7 +4,7 @@ import ( "fmt" "net/http" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) // FromStd converts native http.Handler & http.HandlerFunc to context.Handler. diff --git a/core/handlerconv/from_std_test.go b/core/handlerconv/from_std_test.go deleted file mode 100644 index c024f244fd..0000000000 --- a/core/handlerconv/from_std_test.go +++ /dev/null @@ -1,71 +0,0 @@ -// black-box testing -package handlerconv_test - -import ( - stdContext "context" - "net/http" - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/handlerconv" - "github.com/kataras/iris/v12/httptest" -) - -func TestFromStd(t *testing.T) { - expected := "ok" - std := func(w http.ResponseWriter, r *http.Request) { - _, err := w.Write([]byte(expected)) - if err != nil { - t.Fatal(err) - } - } - - h := handlerconv.FromStd(http.HandlerFunc(std)) - - hFunc := handlerconv.FromStd(std) - - app := iris.New() - app.Get("/handler", h) - app.Get("/func", hFunc) - - e := httptest.New(t, app) - - e.GET("/handler"). - Expect().Status(iris.StatusOK).Body().Equal(expected) - - e.GET("/func"). - Expect().Status(iris.StatusOK).Body().Equal(expected) -} - -func TestFromStdWithNext(t *testing.T) { - basicauth := "secret" - passed := "ok" - - type contextKey string - stdWNext := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { - if username, password, ok := r.BasicAuth(); ok && - username == basicauth && password == basicauth { - ctx := stdContext.WithValue(r.Context(), contextKey("key"), "ok") - next.ServeHTTP(w, r.WithContext(ctx)) - return - } - w.WriteHeader(iris.StatusForbidden) - } - - h := handlerconv.FromStdWithNext(stdWNext) - next := func(ctx *context.Context) { - ctx.WriteString(ctx.Request().Context().Value(contextKey("key")).(string)) - } - - app := iris.New() - app.Get("/handlerwithnext", h, next) - - e := httptest.New(t, app) - - e.GET("/handlerwithnext"). - Expect().Status(iris.StatusForbidden) - - e.GET("/handlerwithnext").WithBasicAuth(basicauth, basicauth). - Expect().Status(iris.StatusOK).Body().Equal(passed) -} diff --git a/core/host/proxy.go b/core/host/proxy.go index 2f9ecee100..044609161d 100644 --- a/core/host/proxy.go +++ b/core/host/proxy.go @@ -8,7 +8,7 @@ import ( "strings" "time" - "github.com/kataras/iris/v12/core/netutil" + "github.com/kataras/iris/core/netutil" ) func singleJoiningSlash(a, b string) string { diff --git a/core/host/proxy_test.go b/core/host/proxy_test.go deleted file mode 100644 index 3ef1886050..0000000000 --- a/core/host/proxy_test.go +++ /dev/null @@ -1,69 +0,0 @@ -// black-box testing -package host_test - -import ( - "net" - "net/url" - "testing" - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/host" - "github.com/kataras/iris/v12/httptest" -) - -func TestProxy(t *testing.T) { - expectedIndex := "ok /" - expectedAbout := "ok /about" - unexpectedRoute := "unexpected" - - // proxySrv := iris.New() - u, err := url.Parse("https://localhost:4444") - if err != nil { - t.Fatalf("%v while parsing url", err) - } - - proxy := host.NewProxy("", u) - - addr := &net.TCPAddr{ - IP: net.IPv4(127, 0, 0, 1), - Port: 0, - } - - listener, err := net.ListenTCP("tcp", addr) - if err != nil { - t.Fatalf("%v while creating listener", err) - } - - go proxy.Serve(listener) // should be localhost/127.0.0.1:80 but travis throws permission denied. - - t.Log(listener.Addr().String()) - <-time.After(time.Second) - t.Log(listener.Addr().String()) - - app := iris.New() - app.Get("/", func(ctx *context.Context) { - ctx.WriteString(expectedIndex) - }) - - app.Get("/about", func(ctx *context.Context) { - ctx.WriteString(expectedAbout) - }) - - app.OnErrorCode(iris.StatusNotFound, func(ctx *context.Context) { - ctx.WriteString(unexpectedRoute) - }) - - l, err := net.Listen("tcp", "localhost:4444") // should be localhost/127.0.0.1:443 but travis throws permission denied. - if err != nil { - t.Fatalf("%v while creating tcp4 listener for new tls local test listener", err) - } - // main server - go app.Run(iris.Listener(httptest.NewLocalTLSListener(l)), iris.WithoutStartupLog) // nolint:errcheck - - e := httptest.NewInsecure(t, httptest.URL("http://"+listener.Addr().String())) - e.GET("/").Expect().Status(iris.StatusOK).Body().Equal(expectedIndex) - e.GET("/about").Expect().Status(iris.StatusOK).Body().Equal(expectedAbout) - e.GET("/notfound").Expect().Status(iris.StatusNotFound).Body().Equal(unexpectedRoute) -} diff --git a/core/host/supervisor.go b/core/host/supervisor.go index dec1b58b4a..bacdbe9ae8 100644 --- a/core/host/supervisor.go +++ b/core/host/supervisor.go @@ -14,7 +14,7 @@ import ( "sync/atomic" "time" - "github.com/kataras/iris/v12/core/netutil" + "github.com/kataras/iris/core/netutil" "golang.org/x/crypto/acme/autocert" ) diff --git a/core/host/supervisor_task_example_test.go b/core/host/supervisor_task_example_test.go deleted file mode 100644 index d0db8edd4a..0000000000 --- a/core/host/supervisor_task_example_test.go +++ /dev/null @@ -1,112 +0,0 @@ -// white-box testing -package host - -import ( - "context" - "fmt" - "log" - "net" - "net/http" - "os" - "time" -) - -func ExampleSupervisor_RegisterOnError() { - su := New(&http.Server{Addr: ":8273", Handler: http.DefaultServeMux}) - - su.RegisterOnError(func(err error) { - fmt.Println(err.Error()) - }) - - su.RegisterOnError(func(err error) { - fmt.Println(err.Error()) - }) - - su.RegisterOnError(func(err error) { - fmt.Println(err.Error()) - }) - - go su.ListenAndServe() - time.Sleep(1 * time.Second) - if err := su.Shutdown(context.TODO()); err != nil { - panic(err) - } - time.Sleep(1 * time.Second) - - // Output: - // http: Server closed - // http: Server closed - // http: Server closed -} - -type myTestTask struct { - restartEvery time.Duration - maxRestarts int - logger *log.Logger -} - -func (m myTestTask) OnServe(host TaskHost) { - host.Supervisor.DeferFlow() // don't exit on underline server's Shutdown. - - ticker := time.NewTicker(m.restartEvery) - defer ticker.Stop() - rans := 0 - for range ticker.C { - exitAfterXRestarts := m.maxRestarts - if rans == exitAfterXRestarts { - m.logger.Println("exit") - ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second) - defer cancel() - _ = host.Supervisor.Shutdown(ctx) // total shutdown - host.Supervisor.RestoreFlow() // free to exit (if shutdown) - return - } - - rans++ - - m.logger.Println(fmt.Sprintf("closed %d times", rans)) - host.Shutdown(context.TODO()) - - startDelay := 2 * time.Second - time.AfterFunc(startDelay, func() { - m.logger.Println("restart") - if err := host.Serve(); err != nil { // restart - panic(err) - } - }) - - } -} - -func ExampleSupervisor_RegisterOnServe() { - h := New(&http.Server{ - Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - }), - }) - - logger := log.New(os.Stdout, "Supervisor: ", 0) - - mytask := myTestTask{ - restartEvery: 3 * time.Second, - maxRestarts: 2, - logger: logger, - } - - h.RegisterOnServe(mytask.OnServe) - - ln, err := net.Listen("tcp4", ":9394") - if err != nil { - panic(err.Error()) - } - - logger.Println("server started...") - h.Serve(ln) - - // Output: - // Supervisor: server started... - // Supervisor: closed 1 times - // Supervisor: restart - // Supervisor: closed 2 times - // Supervisor: restart - // Supervisor: exit -} diff --git a/core/host/supervisor_test.go b/core/host/supervisor_test.go deleted file mode 100644 index 380b7a7fd3..0000000000 --- a/core/host/supervisor_test.go +++ /dev/null @@ -1,119 +0,0 @@ -// white-box testing - -package host - -import ( - "bytes" - "context" - "crypto/tls" - "log" - "net" - "net/http" - "strings" - "sync" - "testing" - - "github.com/iris-contrib/httpexpect/v2" -) - -const ( - debug = false -) - -func newTester(t *testing.T, baseURL string, handler http.Handler) *httpexpect.Expect { - var transporter http.RoundTripper - - if strings.HasPrefix(baseURL, "http") { // means we are testing real serve time - transporter = &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - } else { // means we are testing the handler itself - transporter = httpexpect.NewBinder(handler) - } - - testConfiguration := httpexpect.Config{ - BaseURL: baseURL, - Client: &http.Client{ - Transport: transporter, - Jar: httpexpect.NewJar(), - }, - Reporter: httpexpect.NewAssertReporter(t), - } - - if debug { - testConfiguration.Printers = []httpexpect.Printer{ - httpexpect.NewDebugPrinter(t, true), - } - } - - return httpexpect.WithConfig(testConfiguration) -} - -func testSupervisor(t *testing.T, creator func(*http.Server, []func(TaskHost)) *Supervisor) { - loggerOutput := &bytes.Buffer{} - logger := log.New(loggerOutput, "", 0) - mu := new(sync.RWMutex) - const ( - expectedHelloMessage = "Hello\n" - ) - - // http routing - - expectedBody := "this is the response body\n" - - mux := http.NewServeMux() - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - _, err := w.Write([]byte(expectedBody)) - if err != nil { - t.Fatal(err) - } - }) - - // host (server wrapper and adapter) construction - - srv := &http.Server{Handler: mux, ErrorLog: logger} - addr := "localhost:5525" - // serving - ln, err := net.Listen("tcp4", addr) - if err != nil { - t.Fatal(err) - } - - helloMe := func(_ TaskHost) { - mu.Lock() - logger.Print(expectedHelloMessage) - mu.Unlock() - } - - host := creator(srv, []func(TaskHost){helloMe}) - defer host.Shutdown(context.TODO()) - - go host.Serve(ln) - - // http testsing and various calls - // no need for time sleep because the following will take some time by theirselves - tester := newTester(t, "http://"+addr, mux) - tester.Request("GET", "/").Expect().Status(http.StatusOK).Body().Equal(expectedBody) - - // WARNING: Data Race here because we try to read the logs - // but it's "safe" here. - - // testing Task (recorded) message: - mu.RLock() - got := loggerOutput.String() - mu.RUnlock() - if expectedHelloMessage != got { - t.Fatalf("expected hello Task's message to be '%s' but got '%s'", expectedHelloMessage, got) - } -} - -func TestSupervisor(t *testing.T) { - testSupervisor(t, func(srv *http.Server, tasks []func(TaskHost)) *Supervisor { - su := New(srv) - for _, t := range tasks { - su.RegisterOnServe(t) - } - - return su - }) -} diff --git a/core/host/task.go b/core/host/task.go index 3fd1065228..4741f50c02 100644 --- a/core/host/task.go +++ b/core/host/task.go @@ -13,7 +13,7 @@ import ( "runtime" "time" - "github.com/kataras/iris/v12/core/netutil" + "github.com/kataras/iris/core/netutil" ) // WriteStartupLogOnServe is a task which accepts a logger(io.Writer) diff --git a/core/memstore/memstore_test.go b/core/memstore/memstore_test.go deleted file mode 100644 index 686d48e4a2..0000000000 --- a/core/memstore/memstore_test.go +++ /dev/null @@ -1,167 +0,0 @@ -// white-box testing -package memstore - -import ( - "bytes" - "encoding/json" - "fmt" - "testing" -) - -type myTestObject struct { - Name string `json:"name"` -} - -func TestMuttable(t *testing.T) { - var p Store - - // slice - p.Set("slice", []myTestObject{{"value 1"}, {"value 2"}}) - v := p.Get("slice").([]myTestObject) - v[0].Name = "modified" - - vv := p.Get("slice").([]myTestObject) - if vv[0].Name != "modified" { - t.Fatalf("expected slice to be muttable but caller was not able to change its value") - } - - // map - - p.Set("map", map[string]myTestObject{"key 1": {"value 1"}, "key 2": {"value 2"}}) - vMap := p.Get("map").(map[string]myTestObject) - vMap["key 1"] = myTestObject{"modified"} - - vvMap := p.Get("map").(map[string]myTestObject) - if vvMap["key 1"].Name != "modified" { - t.Fatalf("expected map to be muttable but caller was not able to change its value") - } - - // object pointer of a value, it can change like maps or slices and arrays. - p.Set("objp", &myTestObject{"value"}) - // we expect pointer here, as we set it. - vObjP := p.Get("objp").(*myTestObject) - - vObjP.Name = "modified" - - vvObjP := p.Get("objp").(*myTestObject) - if vvObjP.Name != "modified" { - t.Fatalf("expected objp to be muttable but caller was able to change its value") - } -} - -func TestImmutable(t *testing.T) { - var p Store - - // slice - p.SetImmutable("slice", []myTestObject{{"value 1"}, {"value 2"}}) - v := p.Get("slice").([]myTestObject) - v[0].Name = "modified" - - vv := p.Get("slice").([]myTestObject) - if vv[0].Name == "modified" { - t.Fatalf("expected slice to be immutable but caller was able to change its value") - } - - // map - p.SetImmutable("map", map[string]myTestObject{"key 1": {"value 1"}, "key 2": {"value 2"}}) - vMap := p.Get("map").(map[string]myTestObject) - vMap["key 1"] = myTestObject{"modified"} - - vvMap := p.Get("map").(map[string]myTestObject) - if vvMap["key 1"].Name == "modified" { - t.Fatalf("expected map to be immutable but caller was able to change its value") - } - - // object value, it's immutable at all cases. - p.SetImmutable("obj", myTestObject{"value"}) - vObj := p.Get("obj").(myTestObject) - vObj.Name = "modified" - - vvObj := p.Get("obj").(myTestObject) - if vvObj.Name == "modified" { - t.Fatalf("expected obj to be immutable but caller was able to change its value") - } - - // object pointer of a value, it's immutable at all cases. - p.SetImmutable("objp", &myTestObject{"value"}) - // we expect no pointer here if SetImmutable. - // so it can't be changed by-design - vObjP := p.Get("objp").(myTestObject) - - vObjP.Name = "modified" - - vvObjP := p.Get("objp").(myTestObject) - if vvObjP.Name == "modified" { - t.Fatalf("expected objp to be immutable but caller was able to change its value") - } -} - -func TestImmutableSetOnlyWithSetImmutable(t *testing.T) { - var p Store - - p.SetImmutable("objp", &myTestObject{"value"}) - - p.Set("objp", &myTestObject{"modified"}) - vObjP := p.Get("objp").(myTestObject) - if vObjP.Name == "modified" { - t.Fatalf("caller should not be able to change the immutable entry with a simple `Set`") - } - - p.SetImmutable("objp", &myTestObject{"value with SetImmutable"}) - vvObjP := p.Get("objp").(myTestObject) - if vvObjP.Name != "value with SetImmutable" { - t.Fatalf("caller should be able to change the immutable entry with a `SetImmutable`") - } -} - -func TestGetInt64Default(t *testing.T) { - var p Store - - p.Set("a uint16", uint16(2)) - if v := p.GetInt64Default("a uint16", 0); v != 2 { - t.Fatalf("unexpected value of %d", v) - } -} - -func TestJSON(t *testing.T) { - var p Store - - p.Set("key1", "value1") - p.Set("key2", 2) - p.Set("key3", myTestObject{Name: "makis"}) - - b, err := json.Marshal(p) - if err != nil { - t.Fatal(err) - } - - expectedJSON := []byte(`[{"key":"key1","value":"value1"},{"key":"key2","value":2},{"key":"key3","value":{"name":"makis"}}]`) - - if !bytes.Equal(b, expectedJSON) { - t.Fatalf("expected: %s but got: %s", string(expectedJSON), string(b)) - } - - var newStore Store - if err = json.Unmarshal(b, &newStore); err != nil { - t.Fatal(err) - } - - for i, v := range newStore { - expected, got := p.Get(v.Key), v.ValueRaw - - if ex, g := fmt.Sprintf("%v", expected), fmt.Sprintf("%v", got); ex != g { - if _, isMap := got.(map[string]interface{}); isMap { - // was struct but converted into map (as expected). - b1, _ := json.Marshal(expected) - b2, _ := json.Marshal(got) - - if !bytes.Equal(b1, b2) { - t.Fatalf("[%d] JSON expected: %s but got: %s", i, string(b1), string(b2)) - } - - continue - } - t.Fatalf("[%d] expected: %s but got: %s", i, ex, g) - } - } -} diff --git a/core/netutil/addr_test.go b/core/netutil/addr_test.go deleted file mode 100644 index e5fec93d87..0000000000 --- a/core/netutil/addr_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package netutil - -import ( - "testing" -) - -func TestIsLoopbackHost(t *testing.T) { - tests := []struct { - host string - valid bool - }{ - {"subdomain.127.0.0.1:8080", true}, - {"subdomain.127.0.0.1", true}, - {"subdomain.localhost:8080", true}, - {"subdomain.localhost", true}, - {"subdomain.127.0000.0000.1:8080", true}, - {"subdomain.127.0000.0000.1", true}, - {"subdomain.127.255.255.254:8080", true}, - {"subdomain.127.255.255.254", true}, - - {"subdomain.0000:0:0000::01.1:8080", false}, - {"subdomain.0000:0:0000::01", false}, - {"subdomain.0000:0:0000::01.1:8080", false}, - {"subdomain.0000:0:0000::01", false}, - {"subdomain.0000:0000:0000:0000:0000:0000:0000:0001:8080", true}, - {"subdomain.0000:0000:0000:0000:0000:0000:0000:0001", false}, - - {"subdomain.example:8080", false}, - {"subdomain.example", false}, - {"subdomain.example.com:8080", false}, - {"subdomain.example.com", false}, - {"subdomain.com", false}, - {"subdomain", false}, - {".subdomain", false}, - {"127.0.0.1.com", false}, - } - - for i, tt := range tests { - if expected, got := tt.valid, IsLoopbackHost(tt.host); expected != got { - t.Fatalf("[%d] expected %t but got %t for %s", i, expected, got, tt.host) - } - } -} diff --git a/core/netutil/ip_test.go b/core/netutil/ip_test.go deleted file mode 100644 index 4451b4fa7a..0000000000 --- a/core/netutil/ip_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package netutil - -import ( - "net" - "testing" -) - -func TestIP(t *testing.T) { - privateRanges := []IPRange{ - { - Start: net.ParseIP("10.0.0.0"), - End: net.ParseIP("10.255.255.255"), - }, - { - Start: net.ParseIP("100.64.0.0"), - End: net.ParseIP("100.127.255.255"), - }, - { - Start: net.ParseIP("172.16.0.0"), - End: net.ParseIP("172.31.255.255"), - }, - { - Start: net.ParseIP("192.0.0.0"), - End: net.ParseIP("192.0.0.255"), - }, - { - Start: net.ParseIP("192.168.0.0"), - End: net.ParseIP("192.168.255.255"), - }, - { - Start: net.ParseIP("198.18.0.0"), - End: net.ParseIP("198.19.255.255"), - }, - } - - addresses := []string{ - "201.37.138.59", - "159.117.3.153", - "166.192.97.84", - "225.181.213.210", - "124.50.84.134", - "87.53.250.102", - "106.79.33.62", - "242.120.17.144", - "131.179.101.254", - "103.11.11.174", - "115.97.0.114", - "219.202.120.251", - "37.72.123.120", - "154.94.78.101", - "126.105.144.250", - } - - got, ok := GetIPAddress(addresses, privateRanges) - if !ok { - t.Logf("expected addr to be matched") - } - - if expected := "126.105.144.250"; expected != got { - t.Logf("expected addr to be found: %s but got: %s", expected, got) - } -} diff --git a/core/router/api_builder.go b/core/router/api_builder.go index a781f47c7f..ef8113a633 100644 --- a/core/router/api_builder.go +++ b/core/router/api_builder.go @@ -10,11 +10,11 @@ import ( "strings" "time" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/errgroup" - "github.com/kataras/iris/v12/hero" - "github.com/kataras/iris/v12/macro" - macroHandler "github.com/kataras/iris/v12/macro/handler" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/errgroup" + "github.com/kataras/iris/hero" + "github.com/kataras/iris/macro" + macroHandler "github.com/kataras/iris/macro/handler" ) // MethodNone is a Virtual method diff --git a/core/router/api_builder_benchmark_test.go b/core/router/api_builder_benchmark_test.go deleted file mode 100644 index e709b266de..0000000000 --- a/core/router/api_builder_benchmark_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package router - -import ( - "bytes" - "math/rand" - "strings" - "testing" - "time" - - "github.com/kataras/iris/v12/context" -) - -// -// randStringBytesMaskImprSrc helps us to generate random paths for the test, -// the below piece of code is external, as an answer to a stackoverflow question. -// -// START. -const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" -const ( - letterIdxBits = 6 // 6 bits to represent a letter index - letterIdxMask = 1<= 0; { - if remain == 0 { - cache, remain = src.Int63(), letterIdxMax - } - if idx := int(cache & letterIdxMask); idx < len(letterBytes) { - b[i] = letterBytes[idx] - i-- - } - cache >>= letterIdxBits - remain-- - } - - return strings.ToLower(string(b)) -} - -// END. - -func genPaths(routesLength, minCharLength, maxCharLength int) []string { - // b := new(strings.Builder) - b := new(bytes.Buffer) - paths := make([]string, routesLength) - pathStart := '/' - for i := 0; i < routesLength; i++ { - pathSegmentCharsLength := rand.Intn(maxCharLength-minCharLength) + minCharLength - - b.WriteRune(pathStart) - b.WriteString(randStringBytesMaskImprSrc(pathSegmentCharsLength)) - b.WriteString("/{name:string}/") // sugar. - b.WriteString(randStringBytesMaskImprSrc(pathSegmentCharsLength)) - b.WriteString("/{age:int}/end") - paths[i] = b.String() - - b.Reset() - } - - return paths -} - -// Build 1296(=144*9(the available http methods)) routes -// with up to 2*range(15-42)+ 2 named paths lowercase letters -// and 12 request handlers each. -// -// GOCACHE=off && go test -run=XXX -bench=BenchmarkAPIBuilder$ -benchtime=10s -func BenchmarkAPIBuilder(b *testing.B) { - rand.Seed(time.Now().Unix()) - - noOpHandler := func(ctx *context.Context) {} - handlersPerRoute := make(context.Handlers, 12) - for i := 0; i < cap(handlersPerRoute); i++ { - handlersPerRoute[i] = noOpHandler - } - - routesLength := 144 - // i.e /gzhyweumidvelqewrvoyqmzopvuxli/{name:string}/bibrkratnrrhvsjwsxygfwmqwhcstc/{age:int}/end - paths := genPaths(routesLength, 15, 42) - - api := NewAPIBuilder() - requestHandler := NewDefaultHandler(nil, nil) - - b.ReportAllocs() - b.ResetTimer() - - for i := 0; i < routesLength; i++ { - api.Any(paths[i], handlersPerRoute...) - } - - if err := requestHandler.Build(api); err != nil { - b.Fatal(err) - } - - b.StopTimer() - - b.Logf("%d routes have just builded\n", len(api.GetRoutes())) -} diff --git a/core/router/api_container.go b/core/router/api_container.go index 0504eeb9c8..5ffd94bdb7 100644 --- a/core/router/api_container.go +++ b/core/router/api_container.go @@ -3,9 +3,9 @@ package router import ( "net/http" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/hero" - "github.com/kataras/iris/v12/macro" + "github.com/kataras/iris/context" + "github.com/kataras/iris/hero" + "github.com/kataras/iris/macro" ) // APIContainer is a wrapper of a common `Party` featured by Dependency Injection. diff --git a/core/router/fs.go b/core/router/fs.go index a9224293d8..ce83fe0c18 100644 --- a/core/router/fs.go +++ b/core/router/fs.go @@ -19,7 +19,7 @@ import ( "sync" "time" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) const indexName = "/index.html" diff --git a/core/router/handler.go b/core/router/handler.go index 8dd8fafc9e..18f1fb4997 100644 --- a/core/router/handler.go +++ b/core/router/handler.go @@ -7,10 +7,10 @@ import ( "strings" "time" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/errgroup" - "github.com/kataras/iris/v12/core/netutil" - macroHandler "github.com/kataras/iris/v12/macro/handler" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/errgroup" + "github.com/kataras/iris/core/netutil" + macroHandler "github.com/kataras/iris/macro/handler" "github.com/kataras/golog" "github.com/kataras/pio" diff --git a/core/router/handler_execution_rules.go b/core/router/handler_execution_rules.go index 3ea0b06955..85ddf903d4 100644 --- a/core/router/handler_execution_rules.go +++ b/core/router/handler_execution_rules.go @@ -1,7 +1,7 @@ package router import ( - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) // ExecutionRules gives control to the execution of the route handlers outside of the handlers themselves. diff --git a/core/router/handler_execution_rules_test.go b/core/router/handler_execution_rules_test.go deleted file mode 100644 index ae65a80053..0000000000 --- a/core/router/handler_execution_rules_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package router_test - -import ( - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/router" - "github.com/kataras/iris/v12/httptest" -) - -var ( - finalExecutionRulesResponse = "1234" - - testExecutionResponse = func(t *testing.T, app *iris.Application, path string) { - e := httptest.New(t, app) - e.GET(path).Expect().Status(httptest.StatusOK).Body().Equal(finalExecutionRulesResponse) - } -) - -func writeStringHandler(text string, withNext bool) context.Handler { - return func(ctx *context.Context) { - ctx.WriteString(text) - if withNext { - ctx.Next() - } - } -} - -func TestRouterExecutionRulesForceMain(t *testing.T) { - app := iris.New() - begin := app.Party("/") - begin.SetExecutionRules(router.ExecutionRules{Main: router.ExecutionOptions{Force: true}}) - - // no need of `ctx.Next()` all main handlers should be executed with the Main.Force:True rule. - begin.Get("/", writeStringHandler("12", false), writeStringHandler("3", false), writeStringHandler("4", false)) - - testExecutionResponse(t, app, "/") -} - -func TestRouterExecutionRulesForceBegin(t *testing.T) { - app := iris.New() - begin := app.Party("/begin_force") - begin.SetExecutionRules(router.ExecutionRules{Begin: router.ExecutionOptions{Force: true}}) - - // should execute, begin rule is to force execute them without `ctx.Next()`. - begin.Use(writeStringHandler("1", false)) - begin.Use(writeStringHandler("2", false)) - // begin starts with begin and ends to the main handlers but not last, so this done should not be executed. - begin.Done(writeStringHandler("5", false)) - begin.Get("/", writeStringHandler("3", false), writeStringHandler("4", false)) - - testExecutionResponse(t, app, "/begin_force") -} - -func TestRouterExecutionRulesForceDone(t *testing.T) { - app := iris.New() - done := app.Party("/done_force") - done.SetExecutionRules(router.ExecutionRules{Done: router.ExecutionOptions{Force: true}}) - - // these done should be executed without `ctx.Next()` - done.Done(writeStringHandler("3", false), writeStringHandler("4", false)) - // first with `ctx.Next()`, because Done.Force:True rule will alter the latest of the main handler(s) only. - done.Get("/", writeStringHandler("1", true), writeStringHandler("2", false)) - - // rules should be kept in children. - doneChild := done.Party("/child") - // even if only one, it's the latest, Done.Force:True rule should modify it. - doneChild.Get("/", writeStringHandler("12", false)) - - testExecutionResponse(t, app, "/done_force") - testExecutionResponse(t, app, "/done_force/child") -} - -func TestRouterExecutionRulesShouldNotModifyTheCallersHandlerAndChildrenCanResetExecutionRules(t *testing.T) { - app := iris.New() - app.SetExecutionRules(router.ExecutionRules{Done: router.ExecutionOptions{Force: true}}) - h := writeStringHandler("4", false) - - app.Done(h) - app.Get("/", writeStringHandler("123", false)) - - // remember: the handler stored in var didn't had a `ctx.Next()`, modified its clone above with adding a `ctx.Next()` - // note the "clone" word, the original handler shouldn't be changed. - app.Party("/c").SetExecutionRules(router.ExecutionRules{}).Get("/", h, writeStringHandler("err caller modified!", false)) - - testExecutionResponse(t, app, "/") - - e := httptest.New(t, app) - e.GET("/c").Expect().Status(httptest.StatusOK).Body().Equal("4") // the "should not" should not be written. -} diff --git a/core/router/mime.go b/core/router/mime.go index 1fb50b3614..e5e9ff5e7e 100644 --- a/core/router/mime.go +++ b/core/router/mime.go @@ -4,7 +4,7 @@ import ( "mime" "path/filepath" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) var types = map[string]string{ diff --git a/core/router/party.go b/core/router/party.go index 4051ea495d..c1e065916a 100644 --- a/core/router/party.go +++ b/core/router/party.go @@ -3,9 +3,9 @@ package router import ( "net/http" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/errgroup" - "github.com/kataras/iris/v12/macro" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/errgroup" + "github.com/kataras/iris/macro" ) // Party is just a group joiner of routes which have the same prefix and share same middleware(s) also. diff --git a/core/router/path.go b/core/router/path.go index 5dbb523916..59dc3c77aa 100644 --- a/core/router/path.go +++ b/core/router/path.go @@ -6,10 +6,10 @@ import ( "strconv" "strings" - "github.com/kataras/iris/v12/core/netutil" - "github.com/kataras/iris/v12/macro" - "github.com/kataras/iris/v12/macro/interpreter/ast" - "github.com/kataras/iris/v12/macro/interpreter/lexer" + "github.com/kataras/iris/core/netutil" + "github.com/kataras/iris/macro" + "github.com/kataras/iris/macro/interpreter/ast" + "github.com/kataras/iris/macro/interpreter/lexer" ) // Param receives a parameter name prefixed with the ParamStart symbol. diff --git a/core/router/path_test.go b/core/router/path_test.go deleted file mode 100644 index 5e5c6abc78..0000000000 --- a/core/router/path_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package router - -import ( - "testing" -) - -func TestCleanPath(t *testing.T) { - tests := []struct { - path string - expected string - }{ - { - "/", - "/", - }, - { - "noslashPrefix", - "/noslashPrefix", - }, - { - "slashSuffix/", - "/slashSuffix", - }, - { - "noSlashPrefixAndslashSuffix/", - "/noSlashPrefixAndslashSuffix", - }, - // don't do any clean up inside {}, - // fixes #927. - { - "/total/{year:string regexp(\\d{4})}", - "/total/{year:string regexp(\\d{4})}", - }, - { - "/total/{year:string regexp(\\d{4})}/more", - "/total/{year:string regexp(\\d{4})}/more", - }, - { - "/total/{year:string regexp(\\d{4})}/more/{s:string regexp(\\d{7})}", - "/total/{year:string regexp(\\d{4})}/more/{s:string regexp(\\d{7})}", - }, - { - "/single_no_params", - "/single_no_params", - }, - { - "/single/{id:uint64}", - "/single/{id:uint64}", - }, - } - - for i, tt := range tests { - if expected, got := tt.expected, cleanPath(tt.path); expected != got { - t.Fatalf("[%d] - expected path '%s' but got '%s'", i, expected, got) - } - } -} - -func TestSplitPath(t *testing.T) { - tests := []struct { - path string - expected []string - }{ - { - "/v2/stores/{id:string format(uuid)} /v3", - []string{"/v2/stores/{id:string format(uuid)}", "/v3"}, - }, - { - "/user/{id:uint64} /admin/{id:uint64}", - []string{"/user/{id:uint64}", "/admin/{id:uint64}"}, - }, - { - "/users/{id:int} /admins/{id:int64}", - []string{"/users/{id:int}", "/admins/{id:int64}"}, - }, - { - "/user /admin", - []string{"/user", "/admin"}, - }, - { - "/single_no_params", - []string{"/single_no_params"}, - }, - { - "/single/{id:int}", - []string{"/single/{id:int}"}, - }, - } - - equalSlice := func(s1 []string, s2 []string) bool { - if len(s1) != len(s2) { - return false - } - - for i := range s1 { - if s2[i] != s1[i] { - return false - } - } - - return true - } - - for i, tt := range tests { - paths := splitPath(tt.path) - if expected, got := tt.expected, paths; !equalSlice(expected, got) { - t.Fatalf("[%d] - expected paths '%#v' but got '%#v'", i, expected, got) - } - } -} - -func TestSplitSubdomainAndPath(t *testing.T) { - tests := []struct { - original string - subdomain string - path string - }{ - {"admin./users/42", "admin.", "/users/42"}, - {"static.", "static.", "/"}, - {"static./" + WildcardFileParam(), "static.", "/" + WildcardFileParam()}, - {"//api/users\\42", "", "/api/users/42"}, - {"admin./users//42", "admin.", "/users/42"}, - {"*./users/42/", "*.", "/users/42"}, - } - - for i, tt := range tests { - subdomain, path := splitSubdomainAndPath(tt.original) - - if expected, got := tt.subdomain, subdomain; expected != got { - t.Fatalf("[%d] - expected subdomain '%s' but got '%s'", i, expected, got) - } - if expected, got := tt.path, path; expected != got { - t.Fatalf("[%d] - expected path '%s' but got '%s'", i, expected, got) - } - } -} diff --git a/core/router/route.go b/core/router/route.go index 6b64b1d5da..8e31c5eac9 100644 --- a/core/router/route.go +++ b/core/router/route.go @@ -8,9 +8,9 @@ import ( "strings" "time" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/macro" - "github.com/kataras/iris/v12/macro/handler" + "github.com/kataras/iris/context" + "github.com/kataras/iris/macro" + "github.com/kataras/iris/macro/handler" "github.com/kataras/pio" ) diff --git a/core/router/route_register_rule_test.go b/core/router/route_register_rule_test.go deleted file mode 100644 index 1a581ac24f..0000000000 --- a/core/router/route_register_rule_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package router_test - -import ( - "reflect" - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/core/router" - "github.com/kataras/iris/v12/httptest" -) - -func TestRegisterRule(t *testing.T) { - app := iris.New() - v1 := app.Party("/v1") - v1.SetRegisterRule(iris.RouteSkip) - - getHandler := func(ctx iris.Context) { - ctx.Writef("[get] %s", ctx.Method()) - } - - anyHandler := func(ctx iris.Context) { - ctx.Writef("[any] %s", ctx.Method()) - } - - getRoute := v1.Get("/", getHandler) - v1.Any("/", anyHandler) - if route := v1.Get("/", getHandler); !reflect.DeepEqual(route, getRoute) { - t.Fatalf("expected route to be equal with the original get route") - } - - // test RouteSkip. - e := httptest.New(t, app, httptest.LogLevel("error")) - testRegisterRule(e, "[get] GET") - - // test RouteOverride (default behavior). - v1.SetRegisterRule(iris.RouteOverride) - v1.Any("/", anyHandler) - app.RefreshRouter() - testRegisterRule(e, "[any] GET") - - // test RouteError. - v1.SetRegisterRule(iris.RouteError) - if route := v1.Get("/", getHandler); route != nil { - t.Fatalf("expected duplicated route, with RouteError rule, to be nil but got: %#+v", route) - } - if expected, got := 1, len(v1.GetReporter().Errors); expected != got { - t.Fatalf("expected api builder's errors length to be: %d but got: %d", expected, got) - } -} - -func testRegisterRule(e *httptest.Expect, expectedGetBody string) { - for _, method := range router.AllMethods { - tt := e.Request(method, "/v1").Expect().Status(httptest.StatusOK).Body() - if method == iris.MethodGet { - tt.Equal(expectedGetBody) - } else { - tt.Equal("[any] " + method) - } - } -} - -func TestRegisterRuleOverlap(t *testing.T) { - app := iris.New() - // TODO(@kataras) the overlapping does not work per-party yet, - // it just checks compares from the total app's routes (which is the best possible action to do - // because MVC applications can be separated into different parties too?). - usersRouter := app.Party("/users") - usersRouter.SetRegisterRule(iris.RouteOverlap) - - // second handler will be executed, status will be reset-ed as well, - // stop without data written. - usersRouter.Get("/", func(ctx iris.Context) { - ctx.StopWithStatus(iris.StatusUnauthorized) - }) - usersRouter.Get("/", func(ctx iris.Context) { - ctx.WriteString("data") - }) - - // first handler will be executed, no stop called. - usersRouter.Get("/p1", func(ctx iris.Context) { - ctx.StatusCode(iris.StatusUnauthorized) - }) - usersRouter.Get("/p1", func(ctx iris.Context) { - ctx.WriteString("not written") - }) - - // first handler will be executed, stop but with data sent on default writer - // (body sent cannot be reset-ed here). - usersRouter.Get("/p2", func(ctx iris.Context) { - ctx.StopWithText(iris.StatusUnauthorized, "no access") - }) - usersRouter.Get("/p2", func(ctx iris.Context) { - ctx.WriteString("not written") - }) - - // second will be executed, response can be reset-ed on recording. - usersRouter.Get("/p3", func(ctx iris.Context) { - ctx.Record() - ctx.StopWithText(iris.StatusUnauthorized, "no access") - }) - usersRouter.Get("/p3", func(ctx iris.Context) { - ctx.WriteString("p3 data") - }) - - e := httptest.New(t, app) - - e.GET("/users").Expect().Status(httptest.StatusOK).Body().Equal("data") - e.GET("/users/p1").Expect().Status(httptest.StatusUnauthorized).Body().Equal("Unauthorized") - e.GET("/users/p2").Expect().Status(httptest.StatusUnauthorized).Body().Equal("no access") - e.GET("/users/p3").Expect().Status(httptest.StatusOK).Body().Equal("p3 data") -} diff --git a/core/router/route_test.go b/core/router/route_test.go deleted file mode 100644 index f0b989caa6..0000000000 --- a/core/router/route_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// white-box testing - -package router - -import ( - "testing" - - "github.com/kataras/iris/v12/macro" -) - -func TestRouteStaticPath(t *testing.T) { - tests := []struct { - tmpl string - static string - }{ - { - tmpl: "/files/{file:path}", - static: "/files", - }, - { - tmpl: "/path", - static: "/path", - }, - { - tmpl: "/path/segment", - static: "/path/segment", - }, - { - tmpl: "/path/segment/{n:int}", - static: "/path/segment", - }, - { - tmpl: "/path/{n:uint64}/{n:int}", - static: "/path", - }, - { - tmpl: "/path/{n:uint64}/static", - static: "/path", - }, - { - tmpl: "/{name}", - static: "/", - }, - { - tmpl: "/", - static: "/", - }, - } - - for i, tt := range tests { - route := Route{tmpl: macro.Template{Src: tt.tmpl}} - if expected, got := tt.static, route.StaticPath(); expected != got { - t.Fatalf("[%d:%s] expected static path to be: '%s' but got: '%s'", i, tt.tmpl, expected, got) - } - } -} diff --git a/core/router/router.go b/core/router/router.go index 2fd9071a24..6857575fbe 100644 --- a/core/router/router.go +++ b/core/router/router.go @@ -7,7 +7,7 @@ import ( "strings" "sync" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" "github.com/schollz/closestmatch" ) diff --git a/core/router/router_handlers_order_test.go b/core/router/router_handlers_order_test.go deleted file mode 100644 index 057fbdcf38..0000000000 --- a/core/router/router_handlers_order_test.go +++ /dev/null @@ -1,287 +0,0 @@ -// black-box testing -// -// see _examples/routing/main_test.go for the most common router tests that you may want to see, -// this is a test which makes sure that the APIBuilder's `UseGlobal`, `Use` and `Done` functions are -// working as expected. - -package router_test - -import ( - "fmt" - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/httptest" -) - -// test registering of below handlers -// with a different order but the route's final -// response should be the same at all cases. -var ( - writeHandler = func(s string) iris.Handler { - return func(ctx iris.Context) { - ctx.WriteString(s) - ctx.Next() - } - } - - mainResponse = "main" - mainHandler = writeHandler(mainResponse) - - firstUseResponse = "use1" - firstUseHandler = writeHandler(firstUseResponse) - - secondUseResponse = "use2" - secondUseHandler = writeHandler(secondUseResponse) - - firstUseRouterResponse = "userouter1" - // Use inline handler, no the `writeHandler`, - // because it will be overriden by `secondUseRouterHandler` otherwise, - // look `UseRouter:context.UpsertHandlers` for more. - firstUseRouterHandler = func(ctx iris.Context) { - ctx.WriteString(firstUseRouterResponse) - ctx.Next() - } - - secondUseRouterResponse = "userouter2" - secondUseRouterHandler = writeHandler(secondUseRouterResponse) - - firstUseGlobalResponse = "useglobal1" - firstUseGlobalHandler = writeHandler(firstUseGlobalResponse) - - secondUseGlobalResponse = "useglobal2" - secondUseGlobalHandler = writeHandler(secondUseGlobalResponse) - - firstDoneResponse = "done1" - firstDoneHandler = writeHandler(firstDoneResponse) - - secondDoneResponse = "done2" - secondDoneHandler = func(ctx iris.Context) { - ctx.WriteString(secondDoneResponse) - } - - finalResponse = firstUseRouterResponse + secondUseRouterResponse + firstUseGlobalResponse + secondUseGlobalResponse + - firstUseResponse + secondUseResponse + mainResponse + firstDoneResponse + secondDoneResponse - - testResponse = func(t *testing.T, app *iris.Application, path string) { - t.Helper() - - e := httptest.New(t, app) - e.GET(path).Expect().Status(httptest.StatusOK).Body().Equal(finalResponse) - } -) - -func TestMiddlewareByRouteDef(t *testing.T) { - app := iris.New() - app.UseRouter(firstUseRouterHandler) - app.UseRouter(secondUseRouterHandler) - - app.Get("/mypath", firstUseGlobalHandler, secondUseGlobalHandler, firstUseHandler, secondUseHandler, - mainHandler, firstDoneHandler, secondDoneHandler) - - testResponse(t, app, "/mypath") -} - -func TestMiddlewareByUseAndDoneDef(t *testing.T) { - app := iris.New() - app.UseRouter(firstUseRouterHandler, secondUseRouterHandler) - app.Use(firstUseGlobalHandler, secondUseGlobalHandler, firstUseHandler, secondUseHandler) - app.Done(firstDoneHandler, secondDoneHandler) - - app.Get("/mypath", mainHandler) - - testResponse(t, app, "/mypath") -} - -func TestMiddlewareByUseUseGlobalAndDoneDef(t *testing.T) { - app := iris.New() - - app.Use(firstUseHandler, secondUseHandler) - // if failed then UseGlobal didnt' registered these handlers even before the - // existing middleware. - app.UseGlobal(firstUseGlobalHandler, secondUseGlobalHandler) - app.Done(firstDoneHandler, secondDoneHandler) - - app.UseRouter(firstUseRouterHandler, secondUseRouterHandler) - app.Get("/mypath", mainHandler) - - testResponse(t, app, "/mypath") -} - -func TestMiddlewareByUseDoneAndUseGlobalDef(t *testing.T) { - app := iris.New() - app.UseRouter(firstUseRouterHandler, secondUseRouterHandler) - - app.Use(firstUseHandler, secondUseHandler) - app.Done(firstDoneHandler, secondDoneHandler) - - app.Get("/mypath", mainHandler) - - // if failed then UseGlobal was unable to - // prepend these handlers to the route was registered before - // OR - // when order failed because these should be executed in order, first the firstUseGlobalHandler, - // because they are the same type (global begin handlers) - app.UseGlobal(firstUseGlobalHandler) - app.UseGlobal(secondUseGlobalHandler) - - testResponse(t, app, "/mypath") -} - -func TestMiddlewareByUseGlobalUseAndDoneGlobalDef(t *testing.T) { - app := iris.New() - app.UseRouter(firstUseRouterHandler) - app.UseRouter(secondUseRouterHandler) - - app.UseGlobal(firstUseGlobalHandler) - app.UseGlobal(secondUseGlobalHandler) - app.Use(firstUseHandler, secondUseHandler) - - app.Get("/mypath", mainHandler) - - app.DoneGlobal(firstDoneHandler, secondDoneHandler) - - testResponse(t, app, "/mypath") -} - -func TestMiddlewareByDoneUseAndUseGlobalDef(t *testing.T) { - app := iris.New() - app.UseRouter(firstUseRouterHandler, secondUseRouterHandler) - app.Done(firstDoneHandler, secondDoneHandler) - - app.Use(firstUseHandler, secondUseHandler) - - app.Get("/mypath", mainHandler) - - app.UseGlobal(firstUseGlobalHandler) - app.UseGlobal(secondUseGlobalHandler) - - testResponse(t, app, "/mypath") -} - -func TestUseRouterStopExecution(t *testing.T) { - app := iris.New() - app.UseRouter(func(ctx iris.Context) { - ctx.WriteString("stop") - // no ctx.Next, so the router has not even the chance to work. - }) - app.Get("/", writeHandler("index")) - - e := httptest.New(t, app) - e.GET("/").Expect().Status(iris.StatusOK).Body().Equal("stop") - - app = iris.New() - app.OnErrorCode(iris.StatusForbidden, func(ctx iris.Context) { - ctx.Writef("err: %v", ctx.GetErr()) - }) - app.UseRouter(func(ctx iris.Context) { - ctx.StopWithPlainError(iris.StatusForbidden, fmt.Errorf("custom error")) - // stopped but not data written yet, the error code handler - // should be responsible of it (use StopWithError to write and close). - }) - app.Get("/", writeHandler("index")) - - e = httptest.New(t, app) - e.GET("/").Expect().Status(iris.StatusForbidden).Body().Equal("err: custom error") -} - -func TestUseRouterParentDisallow(t *testing.T) { - const expectedResponse = "no_userouter_allowed" - - app := iris.New() - app.UseRouter(func(ctx iris.Context) { - ctx.WriteString("always") - ctx.Next() - }) - app.Get("/index", func(ctx iris.Context) { - ctx.WriteString(expectedResponse) - }) - - app.SetPartyMatcher(func(p iris.Party, ctx iris.Context) bool { - // modifies the PartyMatcher to not match any UseRouter, - // tests should receive the handlers response alone. - return false - }) - - app.PartyFunc("/", func(p iris.Party) { // it's the same instance of app. - p.UseRouter(func(ctx iris.Context) { - ctx.WriteString("_2") - ctx.Next() - }) - p.Get("/", func(ctx iris.Context) { - ctx.WriteString(expectedResponse) - }) - }) - - app.PartyFunc("/user", func(p iris.Party) { - p.UseRouter(func(ctx iris.Context) { - ctx.WriteString("_3") - ctx.Next() - }) - - p.Get("/", func(ctx iris.Context) { - ctx.WriteString(expectedResponse) - }) - }) - - e := httptest.New(t, app) - e.GET("/index").Expect().Status(iris.StatusOK).Body().Equal(expectedResponse) - e.GET("/").Expect().Status(iris.StatusOK).Body().Equal(expectedResponse) - e.GET("/user").Expect().Status(iris.StatusOK).Body().Equal(expectedResponse) -} - -func TestUseRouterSubdomains(t *testing.T) { - app := iris.New() - app.UseRouter(func(ctx iris.Context) { - if ctx.Subdomain() == "old" { - ctx.Next() // call the router, do not write. - return - } - - // if we write here, it will always give 200 OK, - // even on not registered routes, that's the point at the end, - // full control here when we need it. - ctx.WriteString("always_") - ctx.Next() - }) - - adminAPI := app.Subdomain("admin") - adminAPI.UseRouter(func(ctx iris.Context) { - ctx.WriteString("admin always_") - ctx.Next() - }) - adminAPI.Get("/", func(ctx iris.Context) { - ctx.WriteString("admin") - }) - - adminControlAPI := adminAPI.Subdomain("control") - adminControlAPI.UseRouter(func(ctx iris.Context) { - ctx.WriteString("control admin always_") - ctx.Next() - }) - adminControlAPI.Get("/", func(ctx iris.Context) { - ctx.WriteString("control admin") - }) - - oldAPI := app.Subdomain("old") - oldAPI.Get("/", func(ctx iris.Context) { - ctx.WriteString("chat") - }) - - e := httptest.New(t, app, httptest.URL("http://example.com")) - e.GET("/notfound").Expect().Status(iris.StatusOK).Body().Equal("always_") - - e.GET("/").WithURL("http://admin.example.com").Expect().Status(iris.StatusOK).Body(). - Equal("always_admin always_admin") - - e.GET("/").WithURL("http://control.admin.example.com").Expect().Status(iris.StatusOK).Body(). - Equal("always_admin always_control admin always_control admin") - - // It has a route, and use router just proceeds to the router. - e.GET("/").WithURL("http://old.example.com").Expect().Status(iris.StatusOK).Body(). - Equal("chat") - // this is not a registered path, should fire 404, the UseRouter does not write - // anything to the response writer, so the router has control over it. - e.GET("/notfound").WithURL("http://old.example.com").Expect().Status(iris.StatusNotFound).Body(). - Equal("Not Found") -} diff --git a/core/router/router_subdomain_redirect_wrapper.go b/core/router/router_subdomain_redirect_wrapper.go index 37a7b615ef..99bec72741 100644 --- a/core/router/router_subdomain_redirect_wrapper.go +++ b/core/router/router_subdomain_redirect_wrapper.go @@ -4,8 +4,8 @@ import ( "net/http" "strings" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/netutil" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/netutil" ) type subdomainRedirectWrapper struct { diff --git a/core/router/router_test.go b/core/router/router_test.go deleted file mode 100644 index 535cb7bd33..0000000000 --- a/core/router/router_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package router_test - -import ( - "net/http" - "strings" - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/httptest" -) - -func TestRouteExists(t *testing.T) { - // build the api - app := iris.New() - emptyHandler := func(*context.Context) {} - - // setup the tested routes - app.Handle("GET", "/route-exists", emptyHandler) - app.Handle("POST", "/route-with-param/{param}", emptyHandler) - - // check RouteExists - app.Handle("GET", "/route-test", func(ctx *context.Context) { - if ctx.RouteExists("GET", "/route-not-exists") { - t.Error("Route with path should not exists") - } - - if ctx.RouteExists("POST", "/route-exists") { - t.Error("Route with method should not exists") - } - - if !ctx.RouteExists("GET", "/route-exists") { - t.Error("Route 1 should exists") - } - - if !ctx.RouteExists("POST", "/route-with-param/a-param") { - t.Error("Route 2 should exists") - } - }) - - // run the tests - httptest.New(t, app, httptest.Debug(false)).Request("GET", "/route-test").Expect().Status(iris.StatusOK) -} - -func TestLowercaseRouting(t *testing.T) { - app := iris.New() - app.WrapRouter(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { - // test bottom to begin wrapper, the last ones should execute first. - // The ones that are registered at `Build` state, after this `WrapRouter` call. - // So path should be already lowecased. - if expected, got := strings.ToLower(r.URL.Path), r.URL.Path; expected != got { - t.Fatalf("expected path: %s but got: %s", expected, got) - } - next(w, r) - }) - - h := func(ctx iris.Context) { ctx.WriteString(ctx.Path()) } - - // Register routes. - tests := []string{"/", "/lowercase", "/UPPERCASE", "/Title", "/m1xEd2"} - for _, tt := range tests { - app.Get(tt, h) - } - - app.Configure(iris.WithLowercaseRouting) - // Test routes. - e := httptest.New(t, app) - for _, tt := range tests { - s := strings.ToLower(tt) - e.GET(tt).Expect().Status(httptest.StatusOK).Body().Equal(s) - e.GET(s).Expect().Status(httptest.StatusOK).Body().Equal(s) - e.GET(strings.ToUpper(tt)).Expect().Status(httptest.StatusOK).Body().Equal(s) - } -} diff --git a/core/router/router_wildcard_root_test.go b/core/router/router_wildcard_root_test.go deleted file mode 100644 index becfc3985a..0000000000 --- a/core/router/router_wildcard_root_test.go +++ /dev/null @@ -1,198 +0,0 @@ -// black-box testing -// -// see _examples/routing/main_test.go for the most common router tests that you may want to see, -// this is a test for the new feature that I just coded: wildcard "/{p:path}" on root without conflicts - -package router_test - -import ( - "net/http" - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/httptest" -) - -const ( - same_as_request_path = "same" - from_status_code = "from" - staticPathPrefixBody = "from the static path: " - prefix_static_path_following_by_request_path = "prefix_same" -) - -type testRouteRequest struct { - method string - subdomain string - path string - expectedStatusCode int - expectedBody string -} - -type testRoute struct { - method string - path string - handler context.Handler - requests []testRouteRequest -} - -var h = func(ctx *context.Context) { - ctx.WriteString(ctx.Path()) -} - -var h2 = func(ctx *context.Context) { - ctx.StatusCode(iris.StatusForbidden) // ! 200 but send the body as expected, - // we need that kind of behavior to determinate which handler is executed for routes that - // both having wildcard path but first one is registered on root level. - ctx.WriteString(ctx.Path()) -} - -func h3(ctx *context.Context) { - ctx.Writef(staticPathPrefixBody + ctx.Path()) -} - -func TestRouterWildcardDifferentPrefixPath(t *testing.T) { - tt := []testRoute{ - {"GET", "/s/{p:path}", h, []testRouteRequest{ - {"GET", "", "/s/that/is/wildcard", iris.StatusOK, same_as_request_path}, - {"GET", "", "/s/ok", iris.StatusOK, same_as_request_path}, - }}, - {"GET", "/som/{p:path}", h, []testRouteRequest{ - {"GET", "", "/som/that/is/wildcard", iris.StatusOK, same_as_request_path}, - {"GET", "", "/som/ok", iris.StatusOK, same_as_request_path}, - }}, - {"GET", "/some/{p:path}", h, []testRouteRequest{ - {"GET", "", "/some/that/is/wildcard", iris.StatusOK, same_as_request_path}, - {"GET", "", "/some1/that/is/wildcard", iris.StatusNotFound, from_status_code}, - }}, - } - - testTheRoutes(t, tt, false) -} - -func TestRouterWildcardAndStatic(t *testing.T) { - tt := []testRoute{ - {"GET", "/some/{p:path}", h2, []testRouteRequest{ - {"GET", "", "/some/that/is/wildcard", iris.StatusForbidden, same_as_request_path}, - {"GET", "", "/some/did", iris.StatusForbidden, same_as_request_path}, - {"GET", "", "/some1/that/is/wildcard", iris.StatusNotFound, from_status_code}, - }}, - {"GET", "/some/static", h, []testRouteRequest{ - {"GET", "", "/some/static", iris.StatusOK, same_as_request_path}, - }}, - - {"GET", "/s/{p:path}", h2, []testRouteRequest{ - {"GET", "", "/s/that/is/wildcard", iris.StatusForbidden, same_as_request_path}, - {"GET", "", "/s/did", iris.StatusForbidden, same_as_request_path}, - {"GET", "", "/s1/that/is/wildcard", iris.StatusNotFound, from_status_code}, - }}, - {"GET", "/s/static", h, []testRouteRequest{ - {"GET", "", "/s/static", iris.StatusOK, same_as_request_path}, - }}, - } - - testTheRoutes(t, tt, false) -} - -func TestRouterWildcardRootMany(t *testing.T) { - tt := []testRoute{ - // all routes will be handlded by "h" because we added wildcard to root, - // this feature is very important and can remove noumerous of previous hacks on our apps. - {"GET", "/{p:path}", h, []testRouteRequest{ - {"GET", "", "/this/is/wildcard/on/root", iris.StatusOK, same_as_request_path}, - }}, // mormally, order matters, root should be registered at last - // but we change the front level order algorithm to put last these automatically - // see handler.go - {"GET", "/some/{p:path}", h2, []testRouteRequest{ - {"GET", "", "/some/that/is/wildcard", iris.StatusForbidden, same_as_request_path}, - {"GET", "", "/some/did", iris.StatusForbidden, same_as_request_path}, - }}, - {"GET", "/some/static", h, []testRouteRequest{ - {"GET", "", "/some/static", iris.StatusOK, same_as_request_path}, - }}, - {"GET", "/some1", h, []testRouteRequest{ - {"GET", "", "/some1", iris.StatusOK, same_as_request_path}, - // this will show up because of the first wildcard, as we wanted to do. - {"GET", "", "/some1/that/is/wildcard", iris.StatusOK, same_as_request_path}, - }}, - } - - testTheRoutes(t, tt, false) -} - -func TestRouterWildcardRootManyAndRootStatic(t *testing.T) { - tt := []testRoute{ - // routes that may return 404 will be handled by the below route ("h" handler) because we added wildcard to root, - // this feature is very important and can remove noumerous of previous hacks on our apps. - // - // Static paths and parameters have priority over wildcard, all three types can be registered in the same path prefix. - // - // Remember, all of those routes are registered don't be tricked by the visual appearance of the below test blocks. - {"GET", "/{p:path}", h, []testRouteRequest{ - {"GET", "", "/other2almost/some", iris.StatusOK, same_as_request_path}, - }}, - {"GET", "/static/{p:path}", h, []testRouteRequest{ - {"GET", "", "/static", iris.StatusOK, same_as_request_path}, // HERE<- IF NOT FOUND THEN BACKWARDS TO WILDCARD IF THERE IS ONE, HMM. - {"GET", "", "/static/something/here", iris.StatusOK, same_as_request_path}, - }}, - {"GET", "/", h, []testRouteRequest{ - {"GET", "", "/", iris.StatusOK, same_as_request_path}, - }}, - {"GET", "/other/{paramother:path}", h2, []testRouteRequest{ - // OK and not h2 because of the root wildcard. - {"GET", "", "/other", iris.StatusOK, same_as_request_path}, - {"GET", "", "/other/wildcard", iris.StatusForbidden, same_as_request_path}, - {"GET", "", "/other/wildcard/here", iris.StatusForbidden, same_as_request_path}, - }}, - {"GET", "/other2/{paramothersecond:path}", h2, []testRouteRequest{ - {"GET", "", "/other2/wildcard", iris.StatusForbidden, same_as_request_path}, - {"GET", "", "/other2/more/than/one/path/parts", iris.StatusForbidden, same_as_request_path}, - }}, - {"GET", "/other2/static", h3, []testRouteRequest{ - {"GET", "", "/other2/static", iris.StatusOK, prefix_static_path_following_by_request_path}, - // h2(Forbiddenn) instead of h3 OK because it will be handled by the /other2/{paramothersecond:path}'s handler which gives 403. - {"GET", "", "/other2/staticed", iris.StatusForbidden, same_as_request_path}, - }}, - } - - testTheRoutes(t, tt, false) -} - -func testTheRoutes(t *testing.T, tests []testRoute, debug bool) { - // build the api - app := iris.New() - for _, tt := range tests { - app.Handle(tt.method, tt.path, tt.handler) - } - - // setup the test suite - e := httptest.New(t, app, httptest.Debug(debug)) - - // run the tests - for _, tt := range tests { - for _, req := range tt.requests { - // t.Logf("req: %s:%s\n", tt.method, tt.path) - method := req.method - if method == "" { - method = tt.method - } - ex := e.Request(method, req.path) - if req.subdomain != "" { - ex.WithURL("http://" + req.subdomain + ".localhost:8080") - } - - expectedBody := req.expectedBody - if req.expectedBody == same_as_request_path { - expectedBody = req.path - } - if req.expectedBody == from_status_code { - expectedBody = http.StatusText(req.expectedStatusCode) - } - if req.expectedBody == prefix_static_path_following_by_request_path { - expectedBody = staticPathPrefixBody + req.path - } - - ex.Expect().Status(req.expectedStatusCode).Body().Equal(expectedBody) - } - } -} diff --git a/core/router/router_wrapper_test.go b/core/router/router_wrapper_test.go deleted file mode 100644 index a6d6d2d354..0000000000 --- a/core/router/router_wrapper_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package router - -import ( - "bytes" - "net/http" - "net/http/httptest" - "testing" -) - -func TestMakeWrapperFunc(t *testing.T) { - var ( - firstBody = []byte("1") - secondBody = []byte("2") - mainBody = []byte("3") - expectedBody = append(firstBody, append(secondBody, mainBody...)...) - ) - - pre := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { - w.Header().Set("X-Custom", "data") - next(w, r) - } - - first := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { - w.Write(firstBody) - next(w, r) - } - - second := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { - w.Write(secondBody) - next(w, r) - } - - mainHandler := func(w http.ResponseWriter, r *http.Request) { - w.Write(mainBody) - } - - wrapper := makeWrapperFunc(second, first) - wrapper = makeWrapperFunc(wrapper, pre) - - w := httptest.NewRecorder() - r := httptest.NewRequest("GET", "https://iris-go.com", nil) - wrapper(w, r, mainHandler) - - if got := w.Body.Bytes(); !bytes.Equal(expectedBody, got) { - t.Fatalf("expected boy: %s but got: %s", string(expectedBody), string(got)) - } - - if expected, got := "data", w.Header().Get("X-Custom"); expected != got { - t.Fatalf("expected x-custom header: %s but got: %s", expected, got) - } -} diff --git a/core/router/status_test.go b/core/router/status_test.go deleted file mode 100644 index 8a4a109fe8..0000000000 --- a/core/router/status_test.go +++ /dev/null @@ -1,160 +0,0 @@ -// black-box testing -package router_test - -import ( - "bytes" - "net/http" - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - - "github.com/kataras/iris/v12/httptest" -) - -var defaultErrHandler = func(ctx *context.Context) { - text := http.StatusText(ctx.GetStatusCode()) - ctx.WriteString(text) -} - -func TestOnAnyErrorCode(t *testing.T) { - app := iris.New() - app.Configure(iris.WithFireMethodNotAllowed) - - buff := &bytes.Buffer{} - expectedPrintBeforeExecuteErr := "printed before error" - - // with a middleware - app.OnAnyErrorCode(func(ctx *context.Context) { - buff.WriteString(expectedPrintBeforeExecuteErr) - ctx.Next() - }, defaultErrHandler) - - expectedFoundResponse := "found" - app.Get("/found", func(ctx *context.Context) { - ctx.WriteString(expectedFoundResponse) - }) - - expected407 := "this should be sent, we manage the response response by ourselves" - app.Get("/407", func(ctx *context.Context) { - ctx.Record() - ctx.WriteString(expected407) - ctx.StatusCode(iris.StatusProxyAuthRequired) - }) - - e := httptest.New(t, app) - - e.GET("/found").Expect().Status(iris.StatusOK). - Body().Equal(expectedFoundResponse) - - e.GET("/notfound").Expect().Status(iris.StatusNotFound). - Body().Equal(http.StatusText(iris.StatusNotFound)) - - checkAndClearBuf(t, buff, expectedPrintBeforeExecuteErr) - - e.POST("/found").Expect().Status(iris.StatusMethodNotAllowed). - Body().Equal(http.StatusText(iris.StatusMethodNotAllowed)) - - checkAndClearBuf(t, buff, expectedPrintBeforeExecuteErr) - - e.GET("/407").Expect().Status(iris.StatusProxyAuthRequired). - Body().Equal(expected407) - - // Test Configuration.ResetOnFireErrorCode. - app2 := iris.New() - app2.Configure(iris.WithResetOnFireErrorCode) - - app2.OnAnyErrorCode(func(ctx *context.Context) { - buff.WriteString(expectedPrintBeforeExecuteErr) - ctx.Next() - }, defaultErrHandler) - - app2.Get("/406", func(ctx *context.Context) { - ctx.Record() - ctx.WriteString("this should not be sent, only status text will be sent") - ctx.WriteString("the handler can handle 'rollback' of the text when error code fired because of the recorder") - ctx.StatusCode(iris.StatusNotAcceptable) - }) - - httptest.New(t, app2).GET("/406").Expect().Status(iris.StatusNotAcceptable). - Body().Equal(http.StatusText(iris.StatusNotAcceptable)) - - checkAndClearBuf(t, buff, expectedPrintBeforeExecuteErr) -} - -func checkAndClearBuf(t *testing.T, buff *bytes.Buffer, expected string) { - t.Helper() - - if got := buff.String(); got != expected { - t.Fatalf("expected middleware to run before the error handler, expected: '%s' but got: '%s'", expected, got) - } - - buff.Reset() -} - -func TestPartyOnErrorCode(t *testing.T) { - app := iris.New() - app.Configure(iris.WithFireMethodNotAllowed) - - globalNotFoundResponse := "custom not found" - app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) { - ctx.WriteString(globalNotFoundResponse) - }) - - globalMethodNotAllowedResponse := "global: method not allowed" - app.OnErrorCode(iris.StatusMethodNotAllowed, func(ctx iris.Context) { - ctx.WriteString(globalMethodNotAllowedResponse) - }) - - app.Get("/path", h) - - usersResponse := "users: method allowed" - users := app.Party("/users") - users.OnErrorCode(iris.StatusMethodNotAllowed, func(ctx iris.Context) { - ctx.WriteString(usersResponse) - }) - users.Get("/", h) - write400 := func(ctx iris.Context) { ctx.StatusCode(iris.StatusBadRequest) } - // test setting the error code from a handler. - users.Get("/badrequest", write400) - - usersuserResponse := "users:user: method allowed" - user := users.Party("/{id:int}") - user.OnErrorCode(iris.StatusMethodNotAllowed, func(ctx iris.Context) { - ctx.WriteString(usersuserResponse) - }) - usersuserNotFoundResponse := "users:user: not found" - user.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) { - ctx.WriteString(usersuserNotFoundResponse) - }) - user.Get("/", h) - user.Get("/ab/badrequest", write400) - - friends := users.Party("/friends") - friends.Get("/{id:int}", h) - - e := httptest.New(t, app) - - e.GET("/notfound").Expect().Status(iris.StatusNotFound).Body().Equal(globalNotFoundResponse) - e.POST("/path").Expect().Status(iris.StatusMethodNotAllowed).Body().Equal(globalMethodNotAllowedResponse) - e.GET("/path").Expect().Status(iris.StatusOK).Body().Equal("/path") - - e.POST("/users").Expect().Status(iris.StatusMethodNotAllowed). - Body().Equal(usersResponse) - - e.POST("/users/42").Expect().Status(iris.StatusMethodNotAllowed). - Body().Equal(usersuserResponse) - - e.GET("/users/42").Expect().Status(iris.StatusOK). - Body().Equal("/users/42") - e.GET("/users/ab").Expect().Status(iris.StatusNotFound).Body().Equal(usersuserNotFoundResponse) - // inherit the parent. - e.GET("/users/42/friends/dsa").Expect().Status(iris.StatusNotFound).Body().Equal(usersuserNotFoundResponse) - - // if not registered to the party, then the root is taking action. - e.GET("/users/42/ab/badrequest").Expect().Status(iris.StatusBadRequest).Body().Equal(http.StatusText(iris.StatusBadRequest)) - - // if not registered to the party, and not in root, then just write the status text (fallback behavior) - e.GET("/users/badrequest").Expect().Status(iris.StatusBadRequest). - Body().Equal(http.StatusText(iris.StatusBadRequest)) -} diff --git a/core/router/trie.go b/core/router/trie.go index a22dc769bc..0ebde8cf8f 100644 --- a/core/router/trie.go +++ b/core/router/trie.go @@ -3,7 +3,7 @@ package router import ( "strings" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) const ( diff --git a/doc.go b/doc.go deleted file mode 100644 index db3ee4b9bd..0000000000 --- a/doc.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2017-2020 The Iris Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Iris nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/* -Package iris implements the highest realistic performance, easy to learn Go web framework. -Iris provides a beautifully expressive and easy to use foundation for your next website, API, or distributed app. -Low-level handlers compatible with `net/http` and high-level fastest MVC implementation and handlers dependency injection. -Easy to learn for new gophers and advanced features for experienced, it goes as far as you dive into it! - -Source code and other details for the project are available at GitHub: - - https://github.com/kataras/iris - -Current Version - -12.2.0 - -Installation - -The only requirement is the Go Programming Language, at least version 1.13. - - $ go get github.com/kataras/iris/v12@latest - -Wiki: - - https://github.com/kataras/iris/wiki - -Examples: - - https://github.com/kataras/iris/tree/master/_examples - -Middleware: - - https://github.com/kataras/iris/tree/master/middleware - https://github.com/iris-contrib/middleware - -Home Page: - - https://iris-go.com - -*/ -package iris diff --git a/go.mod b/go.mod index c77486e403..53b1256c0a 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/kataras/iris/v12 +module github.com/kataras/iris go 1.14 diff --git a/hero/binding.go b/hero/binding.go index 18620cc3d8..7c83d0c446 100644 --- a/hero/binding.go +++ b/hero/binding.go @@ -5,7 +5,7 @@ import ( "reflect" "sort" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) // binding contains the Dependency and the Input, it's the result of a function or struct + dependencies. diff --git a/hero/binding_test.go b/hero/binding_test.go deleted file mode 100644 index 40d05c5279..0000000000 --- a/hero/binding_test.go +++ /dev/null @@ -1,548 +0,0 @@ -package hero - -import ( - stdContext "context" - "fmt" - "net/http" - "reflect" - "testing" - "time" - - "github.com/kataras/golog" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/sessions" -) - -var ( - stdContextTyp = reflect.TypeOf((*stdContext.Context)(nil)).Elem() - sessionTyp = reflect.TypeOf((*sessions.Session)(nil)) - timeTyp = reflect.TypeOf((*time.Time)(nil)).Elem() - mapStringsTyp = reflect.TypeOf(map[string][]string{}) -) - -func contextBinding(index int) *binding { - return &binding{ - Dependency: BuiltinDependencies[0], - Input: &Input{Type: BuiltinDependencies[0].DestType, Index: index}, - } -} - -func TestGetBindingsForFunc(t *testing.T) { - type ( - testResponse struct { - Name string `json:"name"` - } - - testRequest struct { - Email string `json:"email"` - } - - testRequest2 struct { - // normally a body can't have two requests but let's test it. - Age int `json:"age"` - } - ) - - var testRequestTyp = reflect.TypeOf(testRequest{}) - - var deps = []*Dependency{ - NewDependency(func(ctx *context.Context) testRequest { return testRequest{Email: "should be ignored"} }), - NewDependency(42), - NewDependency(func(ctx *context.Context) (v testRequest, err error) { - err = ctx.ReadJSON(&v) - return - }), - NewDependency("if two strings requested this should be the last one"), - NewDependency("should not be ignored when requested"), - - // Dependencies like these should always be registered last. - NewDependency(func(ctx *context.Context, input *Input) (newValue reflect.Value, err error) { - wasPtr := input.Type.Kind() == reflect.Ptr - - newValue = reflect.New(indirectType(input.Type)) - ptr := newValue.Interface() - err = ctx.ReadJSON(ptr) - - if !wasPtr { - newValue = newValue.Elem() - } - - return newValue, err - }), - } - - var tests = []struct { - Func interface{} - Expected []*binding - }{ - { // 0 - Func: func(ctx *context.Context) { - ctx.WriteString("t1") - }, - Expected: []*binding{contextBinding(0)}, - }, - { // 1 - Func: func(ctx *context.Context) error { - return fmt.Errorf("err1") - }, - Expected: []*binding{contextBinding(0)}, - }, - { // 2 - Func: func(ctx *context.Context) testResponse { - return testResponse{Name: "name"} - }, - Expected: []*binding{contextBinding(0)}, - }, - { // 3 - Func: func(in testRequest) (testResponse, error) { - return testResponse{Name: "email of " + in.Email}, nil - }, - Expected: []*binding{{Dependency: deps[2], Input: &Input{Index: 0, Type: testRequestTyp}}}, - }, - { // 4 - Func: func(in testRequest) (testResponse, error) { - return testResponse{Name: "not valid "}, fmt.Errorf("invalid") - }, - Expected: []*binding{{Dependency: deps[2], Input: &Input{Index: 0, Type: testRequestTyp}}}, - }, - { // 5 - Func: func(ctx *context.Context, in testRequest) testResponse { - return testResponse{Name: "(with ctx) email of " + in.Email} - }, - Expected: []*binding{contextBinding(0), {Dependency: deps[2], Input: &Input{Index: 1, Type: testRequestTyp}}}, - }, - { // 6 - Func: func(in testRequest, ctx *context.Context) testResponse { // reversed. - return testResponse{Name: "(with ctx) email of " + in.Email} - }, - Expected: []*binding{{Dependency: deps[2], Input: &Input{Index: 0, Type: testRequestTyp}}, contextBinding(1)}, - }, - { // 7 - Func: func(in testRequest, ctx *context.Context, in2 string) testResponse { // reversed. - return testResponse{Name: "(with ctx) email of " + in.Email + "and in2: " + in2} - }, - Expected: []*binding{ - { - Dependency: deps[2], - Input: &Input{Index: 0, Type: testRequestTyp}, - }, - contextBinding(1), - { - Dependency: deps[4], - Input: &Input{Index: 2, Type: reflect.TypeOf("")}, - }, - }, - }, - { // 8 - Func: func(in testRequest, ctx *context.Context, in2, in3 string) testResponse { // reversed. - return testResponse{Name: "(with ctx) email of " + in.Email + " | in2: " + in2 + " in3: " + in3} - }, - Expected: []*binding{ - { - Dependency: deps[2], - Input: &Input{Index: 0, Type: testRequestTyp}, - }, - contextBinding(1), - { - Dependency: deps[len(deps)-3], - Input: &Input{Index: 2, Type: reflect.TypeOf("")}, - }, - { - Dependency: deps[len(deps)-2], - Input: &Input{Index: 3, Type: reflect.TypeOf("")}, - }, - }, - }, - { // 9 - Func: func(ctx *context.Context, in testRequest, in2 testRequest2) testResponse { - return testResponse{Name: fmt.Sprintf("(with ctx) email of %s and in2.Age %d", in.Email, in2.Age)} - }, - Expected: []*binding{ - contextBinding(0), - { - Dependency: deps[2], - Input: &Input{Index: 1, Type: testRequestTyp}, - }, - { - Dependency: deps[len(deps)-1], - Input: &Input{Index: 2, Type: reflect.TypeOf(testRequest2{})}, - }, - }, - }, - { // 10 - Func: func() testResponse { - return testResponse{Name: "empty in, one out"} - }, - Expected: nil, - }, - { // 1 - Func: func(userID string, age int) testResponse { - return testResponse{Name: "in from path parameters"} - }, - Expected: []*binding{ - paramBinding(0, 0, reflect.TypeOf("")), - paramBinding(1, 1, reflect.TypeOf(0)), - }, - }, - // test std context, session, time, request, response writer and headers bindings. - { // 12 - Func: func(stdContext.Context, *sessions.Session, *golog.Logger, time.Time, *http.Request, http.ResponseWriter, http.Header) testResponse { - return testResponse{"builtin deps"} - }, - Expected: []*binding{ - { - Dependency: NewDependency(BuiltinDependencies[1]), - Input: &Input{Index: 0, Type: stdContextTyp}, - }, - { - Dependency: NewDependency(BuiltinDependencies[2]), - Input: &Input{Index: 1, Type: sessionTyp}, - }, - { - Dependency: NewDependency(BuiltinDependencies[3]), - Input: &Input{Index: 2, Type: BuiltinDependencies[3].DestType}, - }, - { - Dependency: NewDependency(BuiltinDependencies[4]), - Input: &Input{Index: 3, Type: timeTyp}, - }, - { - Dependency: NewDependency(BuiltinDependencies[5]), - Input: &Input{Index: 4, Type: BuiltinDependencies[5].DestType}, - }, - { - Dependency: NewDependency(BuiltinDependencies[6]), - Input: &Input{Index: 5, Type: BuiltinDependencies[6].DestType}, - }, - { - Dependency: NewDependency(BuiltinDependencies[7]), - Input: &Input{Index: 6, Type: BuiltinDependencies[7].DestType}, - }, - }, - }, - // test explicitly of http.Header and its underline type map[string][]string which - // but shouldn't be binded to request headers because of the (.Explicitly()), instead - // the map should be binded to our last of "deps" which is is a dynamic functions reads from request body's JSON - // (it's a builtin dependency as well but we declared it to test user dynamic dependencies too). - { // 13 - Func: func(http.Header) testResponse { - return testResponse{"builtin http.Header dep"} - }, - Expected: []*binding{ - { - Dependency: NewDependency(BuiltinDependencies[7]), - Input: &Input{Index: 0, Type: BuiltinDependencies[7].DestType}, - }, - }, - }, - { // 14 - Func: func(map[string][]string) testResponse { - return testResponse{"not dep registered except the dynamic one"} - }, - Expected: []*binding{ - { - Dependency: deps[len(deps)-1], - Input: &Input{Index: 0, Type: mapStringsTyp}, - }, - }, - }, - { // 15 - Func: func(http.Header, map[string][]string) testResponse { - return testResponse{} - }, - Expected: []*binding{ // only http.Header should be binded, we don't have map[string][]string registered. - { - Dependency: NewDependency(BuiltinDependencies[7]), - Input: &Input{Index: 0, Type: BuiltinDependencies[7].DestType}, - }, - { - Dependency: deps[len(deps)-1], - Input: &Input{Index: 1, Type: mapStringsTyp}, - }, - }, - }, - } - - c := New() - for _, dependency := range deps { - c.Register(dependency) - } - - for i, tt := range tests { - bindings := getBindingsForFunc(reflect.ValueOf(tt.Func), c.Dependencies, 0) - - if expected, got := len(tt.Expected), len(bindings); expected != got { - t.Fatalf("[%d] expected bindings length to be: %d but got: %d of: %s", i, expected, got, bindings) - } - - for j, b := range bindings { - if b == nil { - t.Fatalf("[%d:%d] binding is nil!", i, j) - } - - if tt.Expected[j] == nil { - t.Fatalf("[%d:%d] expected dependency was not found!", i, j) - } - - // if expected := tt.Expected[j]; !expected.Equal(b) { - // t.Fatalf("[%d:%d] got unexpected binding:\n%s", i, j, spew.Sdump(expected, b)) - // } - - if expected := tt.Expected[j]; !expected.Equal(b) { - t.Fatalf("[%d:%d] expected binding:\n%s\nbut got:\n%s", i, j, expected, b) - } - } - } -} - -type ( - service interface { - String() string - } - serviceImpl struct{} -) - -var serviceTyp = reflect.TypeOf((*service)(nil)).Elem() - -func (s *serviceImpl) String() string { - return "service" -} - -func TestBindingsForStruct(t *testing.T) { - type ( - controller struct { - Name string - Service service - } - - embedded1 struct { - Age int - } - - embedded2 struct { - Now time.Time - } - - Embedded3 struct { - Age int - } - - Embedded4 struct { - Now time.Time - } - - controllerEmbeddingExported struct { - Embedded3 - Embedded4 - } - - controllerEmbeddingUnexported struct { - embedded1 - embedded2 - } - - controller2 struct { - Emb1 embedded1 - Emb2 embedded2 - } - - controller3 struct { - Emb1 embedded1 - emb2 embedded2 // unused - } - ) - - var deps = []*Dependency{ - NewDependency("name"), - NewDependency(new(serviceImpl)), - } - - var depsForAnonymousEmbedded = []*Dependency{ - NewDependency(42), - NewDependency(time.Now()), - } - - var depsForFieldsOfStruct = []*Dependency{ - NewDependency(embedded1{Age: 42}), - NewDependency(embedded2{time.Now()}), - } - - var depsInterfaces = []*Dependency{ - NewDependency(func(ctx *context.Context) interface{} { - return "name" - }), - } - - var autoBindings = []*binding{ - payloadBinding(0, reflect.TypeOf(embedded1{})), - payloadBinding(1, reflect.TypeOf(embedded2{})), - } - - for _, b := range autoBindings { - b.Input.StructFieldIndex = []int{b.Input.Index} - } - - var tests = []struct { - Value interface{} - Registered []*Dependency - Expected []*binding - }{ - { // 0. - Value: &controller{}, - Registered: deps, - Expected: []*binding{ - { - Dependency: deps[0], - Input: &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf("")}, - }, - { - Dependency: deps[1], - Input: &Input{Index: 1, StructFieldIndex: []int{1}, Type: serviceTyp}, - }, - }, - }, - // 1. test controller with pre-defined variables. - { - Value: &controller{Name: "name_struct", Service: new(serviceImpl)}, - Expected: []*binding{ - { - Dependency: NewDependency("name_struct"), - Input: &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf("")}, - }, - { - Dependency: NewDependency(new(serviceImpl)), - Input: &Input{Index: 1, StructFieldIndex: []int{1}, Type: serviceTyp}, - }, - }, - }, - // 2. test controller with pre-defined variables and other deps with the exact order and value - // (deps from non zero values should be registerded only, if not the Dependency:name_struct will fail for sure). - { - Value: &controller{Name: "name_struct", Service: new(serviceImpl)}, - Registered: deps, - Expected: []*binding{ - { - Dependency: NewDependency("name_struct"), - Input: &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf("")}, - }, - { - Dependency: NewDependency(new(serviceImpl)), - Input: &Input{Index: 1, StructFieldIndex: []int{1}, Type: serviceTyp}, - }, - }, - }, - // 3. test embedded structs with anonymous and exported. - { - Value: &controllerEmbeddingExported{}, - Registered: depsForAnonymousEmbedded, - Expected: []*binding{ - { - Dependency: depsForAnonymousEmbedded[0], - Input: &Input{Index: 0, StructFieldIndex: []int{0, 0}, Type: reflect.TypeOf(0)}, - }, - { - Dependency: depsForAnonymousEmbedded[1], - Input: &Input{Index: 1, StructFieldIndex: []int{1, 0}, Type: reflect.TypeOf(time.Time{})}, - }, - }, - }, - // 4. test for anonymous but not exported (should still be 2, unexported structs are binded). - { - Value: &controllerEmbeddingUnexported{}, - Registered: depsForAnonymousEmbedded, - Expected: []*binding{ - { - Dependency: depsForAnonymousEmbedded[0], - Input: &Input{Index: 0, StructFieldIndex: []int{0, 0}, Type: reflect.TypeOf(0)}, - }, - { - Dependency: depsForAnonymousEmbedded[1], - Input: &Input{Index: 1, StructFieldIndex: []int{1, 0}, Type: reflect.TypeOf(time.Time{})}, - }, - }, - }, - // 5. test for auto-bindings with zero registered. - { - Value: &controller2{}, - Registered: nil, - Expected: autoBindings, - }, - // 6. test for embedded with named fields which should NOT contain any registered deps - // except the two auto-bindings for structs, - { - Value: &controller2{}, - Registered: depsForAnonymousEmbedded, - Expected: autoBindings, - }, // 7. and only embedded struct's fields are readen, otherwise we expect the struct to be a dependency. - { - Value: &controller2{}, - Registered: depsForFieldsOfStruct, - Expected: []*binding{ - { - Dependency: depsForFieldsOfStruct[0], - Input: &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf(embedded1{})}, - }, - { - Dependency: depsForFieldsOfStruct[1], - Input: &Input{Index: 1, StructFieldIndex: []int{1}, Type: reflect.TypeOf(embedded2{})}, - }, - }, - }, - // 8. test one exported and other not exported. - { - Value: &controller3{}, - Registered: []*Dependency{depsForFieldsOfStruct[0]}, - Expected: []*binding{ - { - Dependency: depsForFieldsOfStruct[0], - Input: &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf(embedded1{})}, - }, - }, - }, - // 9. test same as the above but by registering all dependencies. - { - Value: &controller3{}, - Registered: depsForFieldsOfStruct, - Expected: []*binding{ - { - Dependency: depsForFieldsOfStruct[0], - Input: &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf(embedded1{})}, - }, - }, - }, - // 10. test bind an interface{}. - { - Value: &controller{}, - Registered: depsInterfaces, - Expected: []*binding{ - { - Dependency: depsInterfaces[0], - Input: &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf("")}, - }, - }, - }, - } - - for i, tt := range tests { - bindings := getBindingsForStruct(reflect.ValueOf(tt.Value), tt.Registered, 0, nil) - - if expected, got := len(tt.Expected), len(bindings); expected != got { - t.Logf("[%d] expected bindings length to be: %d but got: %d:\n", i, expected, got) - for _, b := range bindings { - t.Logf("\t%s\n", b) - } - t.FailNow() - } - - for j, b := range bindings { - if tt.Expected[j] == nil { - t.Fatalf("[%d:%d] expected dependency was not found!", i, j) - } - - if expected := tt.Expected[j]; !expected.Equal(b) { - t.Fatalf("[%d:%d] expected binding:\n%s\nbut got:\n%s", i, j, expected, b) - } - } - } - -} diff --git a/hero/container.go b/hero/container.go index b82cebcd96..102b520794 100644 --- a/hero/container.go +++ b/hero/container.go @@ -8,8 +8,8 @@ import ( "reflect" "time" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/sessions" + "github.com/kataras/iris/context" + "github.com/kataras/iris/sessions" "github.com/kataras/golog" ) diff --git a/hero/container_test.go b/hero/container_test.go deleted file mode 100644 index 8399ebcf73..0000000000 --- a/hero/container_test.go +++ /dev/null @@ -1,131 +0,0 @@ -package hero_test - -import ( - "fmt" - "reflect" - "testing" - - "github.com/kataras/iris/v12" - . "github.com/kataras/iris/v12/hero" - "github.com/kataras/iris/v12/httptest" -) - -var errTyp = reflect.TypeOf((*error)(nil)).Elem() - -// isError returns true if "typ" is type of `error`. -func isError(typ reflect.Type) bool { - return typ.Implements(errTyp) -} - -type ( - testInput struct { - Name string `json:"name"` - } - - testOutput struct { - ID int `json:"id"` - Name string `json:"name"` - } -) - -var ( - fn = func(id int, in testInput) testOutput { - return testOutput{ - ID: id, - Name: in.Name, - } - } - - expectedOutput = testOutput{ - ID: 42, - Name: "makis", - } - - input = testInput{ - Name: "makis", - } -) - -func TestContainerHandler(t *testing.T) { - app := iris.New() - - c := New() - postHandler := c.Handler(fn) - app.Post("/{id:int}", postHandler) - - e := httptest.New(t, app) - path := fmt.Sprintf("/%d", expectedOutput.ID) - e.POST(path).WithJSON(input).Expect().Status(httptest.StatusOK).JSON().Equal(expectedOutput) -} - -func TestContainerInject(t *testing.T) { - c := New() - - expected := testInput{Name: "test"} - c.Register(expected) - c.Register(&expected) - - // struct value. - var got1 testInput - if err := c.Inject(&got1); err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(expected, got1) { - t.Fatalf("[struct value] expected: %#+v but got: %#+v", expected, got1) - } - - // ptr. - var got2 *testInput - if err := c.Inject(&got2); err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(&expected, got2) { - t.Fatalf("[ptr] expected: %#+v but got: %#+v", &expected, got2) - } - - // register implementation, expect interface. - expected3 := &testServiceImpl{prefix: "prefix: "} - c.Register(expected3) - - var got3 testService - if err := c.Inject(&got3); err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(expected3, got3) { - t.Fatalf("[service] expected: %#+v but got: %#+v", expected3, got3) - } -} - -func TestContainerUseResultHandler(t *testing.T) { - c := New() - resultLogger := func(next ResultHandler) ResultHandler { - return func(ctx iris.Context, v interface{}) error { - t.Logf("%#+v", v) - return next(ctx, v) - } - } - - c.UseResultHandler(resultLogger) - expectedResponse := map[string]interface{}{"injected": true} - c.UseResultHandler(func(next ResultHandler) ResultHandler { - return func(ctx iris.Context, v interface{}) error { - return next(ctx, expectedResponse) - } - }) - c.UseResultHandler(resultLogger) - - handler := c.Handler(func(id int) testOutput { - return testOutput{ - ID: id, - Name: "kataras", - } - }) - - app := iris.New() - app.Get("/{id:int}", handler) - - e := httptest.New(t, app) - e.GET("/42").Expect().Status(httptest.StatusOK).JSON().Equal(expectedResponse) -} diff --git a/hero/dependency.go b/hero/dependency.go index 5d960ff6c7..5a9d872517 100644 --- a/hero/dependency.go +++ b/hero/dependency.go @@ -5,7 +5,7 @@ import ( "reflect" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) type ( diff --git a/hero/dependency_test.go b/hero/dependency_test.go deleted file mode 100644 index b90e341e82..0000000000 --- a/hero/dependency_test.go +++ /dev/null @@ -1,206 +0,0 @@ -package hero_test - -import ( - "fmt" - "reflect" - "testing" - - "github.com/kataras/iris/v12/context" - . "github.com/kataras/iris/v12/hero" -) - -type testDependencyTest struct { - Dependency interface{} - Expected interface{} -} - -func TestDependency(t *testing.T) { - var tests = []testDependencyTest{ - { - Dependency: "myValue", - Expected: "myValue", - }, - { - Dependency: struct{ Name string }{"name"}, - Expected: struct{ Name string }{"name"}, - }, - { - Dependency: func(*context.Context, *Input) (reflect.Value, error) { - return reflect.ValueOf(42), nil - }, - Expected: 42, - }, - { - Dependency: DependencyHandler(func(*context.Context, *Input) (reflect.Value, error) { - return reflect.ValueOf(255), nil - }), - Expected: 255, - }, - { - Dependency: func(*context.Context) (reflect.Value, error) { - return reflect.ValueOf("OK without Input"), nil - }, - Expected: "OK without Input", - }, - { - - Dependency: func(*context.Context, ...string) (reflect.Value, error) { - return reflect.ValueOf("OK variadic ignored"), nil - }, - Expected: "OK variadic ignored", - }, - { - - Dependency: func(*context.Context) reflect.Value { - return reflect.ValueOf("OK without Input and error") - }, - Expected: "OK without Input and error", - }, - { - - Dependency: func(*context.Context, ...int) reflect.Value { - return reflect.ValueOf("OK without error and variadic ignored") - }, - Expected: "OK without error and variadic ignored", - }, - { - - Dependency: func(*context.Context) interface{} { - return "1" - }, - Expected: "1", - }, - { - - Dependency: func(*context.Context) interface{} { - return false - }, - Expected: false, - }, - } - - testDependencies(t, tests) -} - -// Test dependencies that depend on previous one(s). -func TestDependentDependency(t *testing.T) { - msgBody := "prefix: it is a deep dependency" - newMsgBody := msgBody + " new" - var tests = []testDependencyTest{ - // test three level depth and error. - { // 0 - Dependency: &testServiceImpl{prefix: "prefix:"}, - Expected: &testServiceImpl{prefix: "prefix:"}, - }, - { // 1 - Dependency: func(service testService) testMessage { - return testMessage{Body: service.Say("it is a deep") + " dependency"} - }, - Expected: testMessage{Body: msgBody}, - }, - { // 2 - Dependency: func(msg testMessage) string { - return msg.Body - }, - Expected: msgBody, - }, - { // 3 - Dependency: func(msg testMessage) error { - return fmt.Errorf(msg.Body) - }, - Expected: fmt.Errorf(msgBody), - }, - // Test depend on more than one previous registered dependencies and require a before-previous one. - { // 4 - Dependency: func(body string, msg testMessage) string { - if body != msg.Body { - t.Fatalf("body[%s] != msg.Body[%s]", body, msg.Body) - } - - return body + " new" - }, - Expected: newMsgBody, - }, - // Test dependency order by expecting the first returning value and not the later-on registered dependency(#4). - // 5 - { - Dependency: func(body string) string { - return body - }, - Expected: newMsgBody, - }, - } - - testDependencies(t, tests) -} - -func testDependencies(t *testing.T, tests []testDependencyTest) { - t.Helper() - - c := New() - for i, tt := range tests { - d := c.Register(tt.Dependency) - - if d == nil { - t.Fatalf("[%d] expected %#+v to be converted to a valid dependency", i, tt) - } - - val, err := d.Handle(context.NewContext(nil), &Input{}) - - if expectError := isError(reflect.TypeOf(tt.Expected)); expectError { - val = reflect.ValueOf(err) - err = nil - } - - if err != nil { - t.Fatalf("[%d] expected a nil error but got: %v", i, err) - } - - if !val.CanInterface() { - t.Fatalf("[%d] expected output value to be accessible: %T", i, val) - } - - if expected, got := fmt.Sprintf("%#+v", tt.Expected), fmt.Sprintf("%#+v", val.Interface()); expected != got { - t.Fatalf("[%d] expected return value to be:\n%s\nbut got:\n%s", i, expected, got) - } - - // t.Logf("[%d] %s", i, d) - // t.Logf("[%d] output: %#+v", i, val.Interface()) - } -} - -func TestDependentDependencyInheritanceStatic(t *testing.T) { - // Tests the following case #1564: - // Logger - // func(Logger) S1 - // ^ Should be static because Logger - // is a structure, a static dependency. - // - // func(Logger) S2 - // func(S1, S2) S3 - // ^ Should be marked as static dependency - // because everything that depends on are static too. - - type S1 struct { - msg string - } - - type S2 struct { - msg2 string - } - - serviceDep := NewDependency(&testServiceImpl{prefix: "1"}) - d1 := NewDependency(func(t testService) S1 { - return S1{t.Say("2")} - }, serviceDep) - if !d1.Static { - t.Fatalf("d1 dependency should be static: %#+v", d1) - } - - d2 := NewDependency(func(t testService, s S1) S2 { - return S2{"3"} - }, serviceDep, d1) - if !d2.Static { - t.Fatalf("d2 dependency should be static: %#+v", d2) - } -} diff --git a/hero/func_result.go b/hero/func_result.go index 74bd56a3b6..6c25b6a610 100644 --- a/hero/func_result.go +++ b/hero/func_result.go @@ -4,7 +4,7 @@ import ( "reflect" "strings" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" "github.com/fatih/structs" "google.golang.org/protobuf/proto" diff --git a/hero/func_result_test.go b/hero/func_result_test.go deleted file mode 100644 index e89b80d88d..0000000000 --- a/hero/func_result_test.go +++ /dev/null @@ -1,281 +0,0 @@ -package hero_test - -import ( - "errors" - "fmt" - "net/http" - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/httptest" - - . "github.com/kataras/iris/v12/hero" -) - -func GetText() string { - return "text" -} - -func GetStatus() int { - return iris.StatusBadGateway -} - -func GetTextWithStatusOk() (string, int) { - return "OK", iris.StatusOK -} - -// tests should have output arguments mixed -func GetStatusWithTextNotOkBy(first string, second string) (int, string) { - return iris.StatusForbidden, "NOT_OK_" + first + second -} - -func GetTextAndContentType() (string, string) { - return "text", "text/html" -} - -type testCustomResult struct { - HTML string -} - -// The only one required function to make that a custom Response dispatcher. -func (r testCustomResult) Dispatch(ctx iris.Context) { - _, _ = ctx.HTML(r.HTML) -} - -func GetCustomResponse() testCustomResult { - return testCustomResult{"text"} -} - -func GetCustomResponseWithStatusOk() (testCustomResult, int) { - return testCustomResult{"OK"}, iris.StatusOK -} - -func GetCustomResponseWithStatusNotOk() (testCustomResult, int) { - return testCustomResult{"internal server error"}, iris.StatusInternalServerError -} - -type testCustomStruct struct { - Name string `json:"name" xml:"name"` - Age int `json:"age" xml:"age"` -} - -func GetCustomStruct() testCustomStruct { - return testCustomStruct{"Iris", 2} -} - -func GetCustomStructWithStatusNotOk() (testCustomStruct, int) { - return testCustomStruct{"Iris", 2}, iris.StatusInternalServerError -} - -func GetCustomStructWithContentType() (testCustomStruct, string) { - return testCustomStruct{"Iris", 2}, "text/xml" -} - -func GetCustomStructWithError(ctx iris.Context) (s testCustomStruct, err error) { - s = testCustomStruct{"Iris", 2} - if ctx.URLParamExists("err") { - err = errors.New("omit return of testCustomStruct and fire error") - } - - // it should send the testCustomStruct as JSON if error is nil - // otherwise it should fire the default error(BadRequest) with the error's text. - return -} - -type err struct { - Status int `json:"status_code"` - Message string `json:"message"` -} - -func (e err) Dispatch(ctx iris.Context) { - // write the status code based on the err's StatusCode. - ctx.StatusCode(e.Status) - // send to the client the whole object as json - _, _ = ctx.JSON(e) -} - -func GetCustomErrorAsDispatcher() err { - return err{iris.StatusBadRequest, "this is my error as json"} -} - -func GetCustomTypedNilEmptyResponse() iris.Map { - return nil -} - -func GetCustomTypedPtrNilEmptyResponse() *iris.Map { - return nil -} - -func GetCustomMapNilEmptyResponse() map[string]interface{} { - return nil -} - -func GetCustomPtrStructNilEmptyResponse() *testCustomStruct { - return nil -} - -func TestFuncResult(t *testing.T) { - app := iris.New() - h := New() - // for any 'By', by is not required but we use this suffix here, like controllers - // to make it easier for the future to resolve if any bug. - // add the binding for path parameters. - - app.Get("/text", h.Handler(GetText)) - app.Get("/status", h.Handler(GetStatus)) - app.Get("/text/with/status/ok", h.Handler(GetTextWithStatusOk)) - app.Get("/status/with/text/not/ok/{first}/{second}", h.Handler(GetStatusWithTextNotOkBy)) - app.Get("/text/and/content/type", h.Handler(GetTextAndContentType)) - // - app.Get("/custom/response", h.Handler(GetCustomResponse)) - app.Get("/custom/response/with/status/ok", h.Handler(GetCustomResponseWithStatusOk)) - app.Get("/custom/response/with/status/not/ok", h.Handler(GetCustomResponseWithStatusNotOk)) - // - app.Get("/custom/struct", h.Handler(GetCustomStruct)) - app.Get("/custom/struct/with/status/not/ok", h.Handler(GetCustomStructWithStatusNotOk)) - app.Get("/custom/struct/with/content/type", h.Handler(GetCustomStructWithContentType)) - app.Get("/custom/struct/with/error", h.Handler(GetCustomStructWithError)) - app.Get("/custom/error/as/dispatcher", h.Handler(GetCustomErrorAsDispatcher)) - - app.Get("/custom/nil/typed", h.Handler(GetCustomTypedNilEmptyResponse)) - app.Get("/custom/nil/typed/ptr", h.Handler(GetCustomTypedPtrNilEmptyResponse)) - app.Get("/custom/nil/map", h.Handler(GetCustomMapNilEmptyResponse)) - app.Get("/custom/nil/struct", h.Handler(GetCustomPtrStructNilEmptyResponse)) - - e := httptest.New(t, app) - - e.GET("/text").Expect().Status(iris.StatusOK). - Body().Equal("text") - - e.GET("/status").Expect().Status(iris.StatusBadGateway) - - e.GET("/text/with/status/ok").Expect().Status(iris.StatusOK). - Body().Equal("OK") - - e.GET("/status/with/text/not/ok/first/second").Expect().Status(iris.StatusForbidden). - Body().Equal("NOT_OK_firstsecond") - // Author's note: <-- if that fails means that the last binder called for both input args, - // see path_param_binder.go - - e.GET("/text/and/content/type").Expect().Status(iris.StatusOK). - ContentType("text/html", "utf-8"). - Body().Equal("text") - - e.GET("/custom/response").Expect().Status(iris.StatusOK). - ContentType("text/html", "utf-8"). - Body().Equal("text") - e.GET("/custom/response/with/status/ok").Expect().Status(iris.StatusOK). - ContentType("text/html", "utf-8"). - Body().Equal("OK") - e.GET("/custom/response/with/status/not/ok").Expect().Status(iris.StatusInternalServerError). - ContentType("text/html", "utf-8"). - Body().Equal("internal server error") - - expectedResultFromCustomStruct := map[string]interface{}{ - "name": "Iris", - "age": 2, - } - e.GET("/custom/struct").Expect().Status(iris.StatusOK). - JSON().Equal(expectedResultFromCustomStruct) - e.GET("/custom/struct/with/status/not/ok").Expect().Status(iris.StatusInternalServerError). - JSON().Equal(expectedResultFromCustomStruct) - e.GET("/custom/struct/with/content/type").Expect().Status(iris.StatusOK). - ContentType("text/xml", "utf-8") - e.GET("/custom/struct/with/error").Expect().Status(iris.StatusOK). - JSON().Equal(expectedResultFromCustomStruct) - e.GET("/custom/struct/with/error").WithQuery("err", true).Expect(). - Status(iris.StatusBadRequest). // the default status code if error is not nil - // the content should be not JSON it should be the status code's text - // it will fire the error's text - Body().Equal("omit return of testCustomStruct and fire error") - - e.GET("/custom/error/as/dispatcher").Expect(). - Status(iris.StatusBadRequest). // the default status code if error is not nil - // the content should be not JSON it should be the status code's text - // it will fire the error's text - JSON().Equal(err{iris.StatusBadRequest, "this is my error as json"}) - - // its result is nil should give an empty response but content-type is set correctly. - e.GET("/custom/nil/typed").Expect(). - Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty() - e.GET("/custom/nil/typed/ptr").Expect(). - Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty() - e.GET("/custom/nil/map").Expect(). - Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty() - e.GET("/custom/nil/struct").Expect(). - Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty() -} - -type ( - testPreflightRequest struct { - FailCode int `json:"fail_code"` // for the sake of the test. - } - - testPreflightResponse struct { - Code int - Message string - } -) - -func (r testPreflightResponse) Preflight(ctx iris.Context) error { - if r.Code == httptest.StatusInternalServerError { - return fmt.Errorf("custom error") - } - - ctx.StatusCode(r.Code) - return nil -} - -func TestPreflightResult(t *testing.T) { - app := iris.New() - c := New() - handler := c.Handler(func(in testPreflightRequest) testPreflightResponse { - return testPreflightResponse{Code: in.FailCode, Message: http.StatusText(in.FailCode)} - }) - app.Post("/", handler) - handler2 := c.Handler(func(in testInput) (int, testOutput) { - return httptest.StatusAccepted, testOutput{Name: in.Name} - }) - app.Post("/alternative", handler2) - - e := httptest.New(t, app) - - expected1 := testPreflightResponse{Code: httptest.StatusOK, Message: "OK"} - e.POST("/").WithJSON(testPreflightRequest{FailCode: expected1.Code}). - Expect().Status(httptest.StatusOK).JSON().Equal(expected1) - - expected2 := testPreflightResponse{Code: httptest.StatusBadRequest, Message: "Bad Request"} - e.POST("/").WithJSON(testPreflightRequest{FailCode: expected2.Code}). - Expect().Status(httptest.StatusBadRequest).JSON().Equal(expected2) - - // Test error returned from Preflight. - e.POST("/").WithJSON(testPreflightRequest{FailCode: httptest.StatusInternalServerError}). - Expect().Status(httptest.StatusBadRequest).Body().Equal("custom error") - - // Can be done without Preflight as the second output argument can be a status code. - expected4 := testOutput{Name: "my_name"} - e.POST("/alternative").WithJSON(testInput{expected4.Name}). - Expect().Status(httptest.StatusAccepted).JSON().Equal(expected4) -} - -func TestResponseErr(t *testing.T) { - app := iris.New() - var expectedErr = errors.New("response error") - - app.OnAnyErrorCode(func(ctx iris.Context) { - err := ctx.GetErr() - if err != expectedErr { - t.Fatalf("expected error value does not match") - } - - ctx.WriteString(err.Error()) - }) - - app.ConfigureContainer().Get("/", func() Response { - return Response{Code: iris.StatusBadGateway, Err: expectedErr} - }) - - e := httptest.New(t, app) - e.GET("/").Expect().Status(iris.StatusBadGateway).Body().Equal("response error") -} diff --git a/hero/handler.go b/hero/handler.go index 673a8ece3d..d3b02a8db6 100644 --- a/hero/handler.go +++ b/hero/handler.go @@ -4,7 +4,7 @@ import ( "fmt" "reflect" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) type ( diff --git a/hero/handler_test.go b/hero/handler_test.go deleted file mode 100644 index f3923b25db..0000000000 --- a/hero/handler_test.go +++ /dev/null @@ -1,287 +0,0 @@ -package hero_test - -import ( - "fmt" - "testing" - - "github.com/kataras/iris/v12" - . "github.com/kataras/iris/v12/hero" - "github.com/kataras/iris/v12/httptest" -) - -// dynamic func -type testUserStruct struct { - ID int64 `json:"id" form:"id" url:"id"` - Username string `json:"username" form:"username" url:"username"` -} - -func testBinderFunc(ctx iris.Context) testUserStruct { - id, _ := ctx.Params().GetInt64("id") - username := ctx.Params().Get("username") - return testUserStruct{ - ID: id, - Username: username, - } -} - -// service -type ( - // these testService and testServiceImpl could be in lowercase, unexported - // but the `Say` method should be exported however we have those exported - // because of the controller handler test. - testService interface { - Say(string) string - } - testServiceImpl struct { - prefix string - } -) - -func (s *testServiceImpl) Say(message string) string { - return s.prefix + " " + message -} - -var ( - // binders, as user-defined - testBinderFuncUserStruct = testBinderFunc - testBinderService = &testServiceImpl{prefix: "say"} - testBinderFuncParam = func(ctx iris.Context) string { - return ctx.Params().Get("param") - } - - // consumers - // a context as first input arg, which is not needed to be binded manually, - // and a user struct which is binded to the input arg by the #1 func(ctx) any binder. - testConsumeUserHandler = func(ctx iris.Context, user testUserStruct) { - ctx.JSON(user) - } - - // just one input arg, the service which is binded by the #2 service binder. - testConsumeServiceHandler = func(service testService) string { - return service.Say("something") - } - // just one input arg, a standar string which is binded by the #3 func(ctx) any binder. - testConsumeParamHandler = func(myParam string) string { - return "param is: " + myParam - } -) - -func TestHandler(t *testing.T) { - Register(testBinderFuncUserStruct) - Register(testBinderService) - Register(testBinderFuncParam) - var ( - h1 = Handler(testConsumeUserHandler) - h2 = Handler(testConsumeServiceHandler) - h3 = Handler(testConsumeParamHandler) - ) - - testAppWithHeroHandlers(t, h1, h2, h3) -} - -func testAppWithHeroHandlers(t *testing.T, h1, h2, h3 iris.Handler) { - app := iris.New() - app.Get("/{id:int64}/{username:string}", h1) - app.Get("/service", h2) - app.Get("/param/{param:string}", h3) - - expectedUser := testUserStruct{ - ID: 42, - Username: "kataras", - } - - e := httptest.New(t, app) - // 1 - e.GET(fmt.Sprintf("/%d/%s", expectedUser.ID, expectedUser.Username)).Expect().Status(httptest.StatusOK). - JSON().Equal(expectedUser) - // 2 - e.GET("/service").Expect().Status(httptest.StatusOK). - Body().Equal("say something") - // 3 - e.GET("/param/the_param_value").Expect().Status(httptest.StatusOK). - Body().Equal("param is: the_param_value") -} - -// TestBindFunctionAsFunctionInputArgument tests to bind -// a whole dynamic function based on the current context -// as an input argument in the hero handler's function. -func TestBindFunctionAsFunctionInputArgument(t *testing.T) { - app := iris.New() - postsBinder := func(ctx iris.Context) func(string) string { - return ctx.PostValue // or FormValue, the same here. - } - - h := New(postsBinder).Handler(func(get func(string) string) string { - // send the `ctx.PostValue/FormValue("username")` value - // to the client. - return get("username") - }) - - app.Post("/", h) - - e := httptest.New(t, app) - - expectedUsername := "kataras" - e.POST("/").WithFormField("username", expectedUsername). - Expect().Status(iris.StatusOK).Body().Equal(expectedUsername) -} - -func TestPayloadBinding(t *testing.T) { - h := New() - - ptrHandler := h.Handler(func(input *testUserStruct /* ptr */) string { - return input.Username - }) - - valHandler := h.Handler(func(input testUserStruct) string { - return input.Username - }) - - app := iris.New() - app.Get("/", ptrHandler) - app.Post("/", ptrHandler) - app.Post("/2", valHandler) - - e := httptest.New(t, app) - - // JSON - e.POST("/").WithJSON(iris.Map{"username": "makis"}).Expect().Status(httptest.StatusOK).Body().Equal("makis") - e.POST("/2").WithJSON(iris.Map{"username": "kataras"}).Expect().Status(httptest.StatusOK).Body().Equal("kataras") - - // FORM (url-encoded) - e.POST("/").WithFormField("username", "makis").Expect().Status(httptest.StatusOK).Body().Equal("makis") - // FORM (multipart) - e.POST("/").WithMultipart().WithFormField("username", "makis").Expect().Status(httptest.StatusOK).Body().Equal("makis") - - // POST URL query. - e.POST("/").WithQuery("username", "makis").Expect().Status(httptest.StatusOK).Body().Equal("makis") - // GET URL query. - e.GET("/").WithQuery("username", "makis").Expect().Status(httptest.StatusOK).Body().Equal("makis") -} - -/* Author's notes: -If aksed or required by my company, make the following test to pass but think downsides of code complexity and performance-cost -before begin the implementation of it. -- Dependencies without depending on other values can be named "root-level dependencies" -- Dependencies could be linked (a new .DependsOn?) to a "root-level dependency"(or by theirs same-level deps too?) with much - more control if "root-level dependencies" are named, e.g.: - b.Register("db", &myDBImpl{}) - b.Register("user_dep", func(db myDB) User{...}).DependsOn("db") - b.Handler(func(user User) error{...}) - b.Handler(func(ctx iris.Context, reuseDB myDB) {...}) -Why linked over automatically? Because more than one dependency can implement the same input and -end-user does not care about ordering the registered ones. -Link with `DependsOn` SHOULD be optional, if exists then limit the available dependencies, -`DependsOn` SHOULD accept comma-separated values, e.g. "db, otherdep" and SHOULD also work -by calling it multiple times i.e `Depends("db").DependsOn("otherdep")`. -Handlers should also be able to explicitly limit the list of -their available dependencies per-handler, a `.DependsOn` feature SHOULD exist there too. - -Also, note that with the new implementation a `*hero.Input` value can be accepted on dynamic dependencies, -that value contains an `Options.Dependencies` field which lists all the registered dependencies, -so, in theory, end-developers could achieve same results by hand-code(inside the dependency's function body). - -26 Feb 2020. Gerasimos Maropoulos -______________________________________________ - -29 Feb 2020. It's done. -*/ - -type testMessage struct { - Body string -} - -func TestDependentDependencies(t *testing.T) { - b := New() - b.Register(&testServiceImpl{prefix: "prefix:"}) - b.Register(func(service testService) testMessage { - return testMessage{Body: service.Say("it is a deep") + " dependency"} - }) - var ( - h1 = b.Handler(func(msg testMessage) string { - return msg.Body - }) - h2 = b.Handler(func(reuse testService) string { - return reuse.Say("message") - }) - ) - - app := iris.New() - app.Get("/h1", h1) - app.Get("/h2", h2) - - e := httptest.New(t, app) - e.GET("/h1").Expect().Status(httptest.StatusOK).Body().Equal("prefix: it is a deep dependency") - e.GET("/h2").Expect().Status(httptest.StatusOK).Body().Equal("prefix: message") -} - -func TestHandlerPathParams(t *testing.T) { - // See white box `TestPathParams` test too. - // All cases should pass. - app := iris.New() - handler := func(id uint64) string { - return fmt.Sprintf("%d", id) - } - - app.Party("/users").ConfigureContainer(func(api *iris.APIContainer) { - api.Get("/{id:uint64}", handler) - }) - - app.Party("/editors/{id:uint64}").ConfigureContainer(func(api *iris.APIContainer) { - api.Get("/", handler) - }) - - // should receive the last one, as we expected only one useful for MVC (there is a similar test there too). - app.ConfigureContainer().Get("/{ownerID:uint64}/book/{booKID:uint64}", handler) - - e := httptest.New(t, app) - - for _, testReq := range []*httptest.Request{ - e.GET("/users/42"), - e.GET("/editors/42"), - e.GET("/1/book/42"), - } { - testReq.Expect().Status(httptest.StatusOK).Body().Equal("42") - } -} - -func TestRegisterDependenciesFromContext(t *testing.T) { - // Tests serve-time struct dependencies through a common Iris middleware. - app := iris.New() - app.Use(func(ctx iris.Context) { - ctx.RegisterDependency(testUserStruct{Username: "kataras"}) - ctx.Next() - }) - app.Use(func(ctx iris.Context) { - ctx.RegisterDependency(&testServiceImpl{prefix: "say"}) - ctx.Next() - }) - - app.ConfigureContainer(func(api *iris.APIContainer) { - api.Get("/", func(u testUserStruct) string { - return u.Username - }) - - api.Get("/service", func(s *testServiceImpl) string { - return s.Say("hello") - }) - - // Note: we are not allowed to pass the service as an interface here - // because the container will, correctly, panic because it will expect - // a dependency to be registered before server ran. - api.Get("/both", func(s *testServiceImpl, u testUserStruct) string { - return s.Say(u.Username) - }) - - api.Get("/non", func() string { - return "nothing" - }) - }) - - e := httptest.New(t, app) - - e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal("kataras") - e.GET("/service").Expect().Status(httptest.StatusOK).Body().Equal("say hello") - e.GET("/both").Expect().Status(httptest.StatusOK).Body().Equal("say kataras") - e.GET("/non").Expect().Status(httptest.StatusOK).Body().Equal("nothing") -} diff --git a/hero/param_test.go b/hero/param_test.go deleted file mode 100644 index baa34d92fa..0000000000 --- a/hero/param_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package hero - -import ( - "testing" - - "github.com/kataras/iris/v12/context" -) - -func TestPathParams(t *testing.T) { - got := "" - h := New() - handler := h.Handler(func(firstname string, lastname string) { - got = firstname + lastname - }) - - h.Register(func(ctx *context.Context) func() string { return func() string { return "" } }) - handlerWithOther := h.Handler(func(f func() string, firstname string, lastname string) { - got = f() + firstname + lastname - }) - - handlerWithOtherBetweenThem := h.Handler(func(firstname string, f func() string, lastname string) { - got = firstname + lastname - }) - - ctx := context.NewContext(nil) - ctx.Params().Set("firstname", "Gerasimos") - ctx.Params().Set("lastname", "Maropoulos") - handler(ctx) - expected := "GerasimosMaropoulos" - if got != expected { - t.Fatalf("[0] expected the params 'firstname' + 'lastname' to be '%s' but got '%s'", expected, got) - } - - got = "" - handlerWithOther(ctx) - expected = "GerasimosMaropoulos" - if got != expected { - t.Fatalf("[1] expected the params 'firstname' + 'lastname' to be '%s' but got '%s'", expected, got) - } - - got = "" - handlerWithOtherBetweenThem(ctx) - expected = "GerasimosMaropoulos" - if got != expected { - t.Fatalf("[2] expected the params 'firstname' + 'lastname' to be '%s' but got '%s'", expected, got) - } -} diff --git a/hero/reflect.go b/hero/reflect.go index f7c8b18693..2102a73c74 100644 --- a/hero/reflect.go +++ b/hero/reflect.go @@ -4,7 +4,7 @@ import ( "net" "reflect" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) func valueOf(v interface{}) reflect.Value { diff --git a/hero/reflect_test.go b/hero/reflect_test.go deleted file mode 100644 index 6f10772ba3..0000000000 --- a/hero/reflect_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package hero - -import ( - "reflect" - "testing" -) - -type testInterface interface { - Get() string -} - -var testInterfaceTyp = reflect.TypeOf((*testInterface)(nil)).Elem() - -type testImplPtr struct{} - -func (*testImplPtr) Get() string { return "get_ptr" } - -type testImpl struct{} - -func (testImpl) Get() string { return "get" } - -func TestEqualTypes(t *testing.T) { - of := reflect.TypeOf - - var tests = map[reflect.Type]reflect.Type{ - of("string"): of("input"), - of(42): of(10), - testInterfaceTyp: testInterfaceTyp, - of(new(testImplPtr)): testInterfaceTyp, - of(new(testImpl)): testInterfaceTyp, - of(testImpl{}): testInterfaceTyp, - } - - for binding, input := range tests { - if !equalTypes(binding, input) { - t.Fatalf("expected type of: %s to be equal to the binded one of: %s", input, binding) - } - } -} diff --git a/hero/struct.go b/hero/struct.go index 2ee16f77f8..c0122ac253 100644 --- a/hero/struct.go +++ b/hero/struct.go @@ -4,7 +4,7 @@ import ( "fmt" "reflect" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) // Sorter is the type for sort customization of a struct's fields diff --git a/hero/struct_test.go b/hero/struct_test.go deleted file mode 100644 index 5401f7bdd8..0000000000 --- a/hero/struct_test.go +++ /dev/null @@ -1,121 +0,0 @@ -package hero_test - -import ( - "errors" - "fmt" - "testing" - - "github.com/kataras/iris/v12" - . "github.com/kataras/iris/v12/hero" - "github.com/kataras/iris/v12/httptest" -) - -type testStruct struct { - Ctx iris.Context -} - -func (c *testStruct) MyHandler(name string) testOutput { - return fn(42, testInput{Name: name}) -} - -func (c *testStruct) MyHandler2(id int, in testInput) testOutput { - return fn(id, in) -} - -func (c *testStruct) MyHandler3(in testInput) testOutput { - return fn(42, in) -} - -func (c *testStruct) MyHandler4() { - c.Ctx.WriteString("MyHandler4") -} - -func TestStruct(t *testing.T) { - app := iris.New() - - b := New() - s := b.Struct(&testStruct{}, 0) - - postHandler := s.MethodHandler("MyHandler", 0) // fallbacks such as {path} and {string} should registered first when same path. - app.Post("/{name:string}", postHandler) - postHandler2 := s.MethodHandler("MyHandler2", 0) - app.Post("/{id:int}", postHandler2) - postHandler3 := s.MethodHandler("MyHandler3", 0) - app.Post("/myHandler3", postHandler3) - getHandler := s.MethodHandler("MyHandler4", 0) - app.Get("/myHandler4", getHandler) - - e := httptest.New(t, app) - e.POST("/" + input.Name).Expect().Status(httptest.StatusOK).JSON().Equal(expectedOutput) - path := fmt.Sprintf("/%d", expectedOutput.ID) - e.POST(path).WithJSON(input).Expect().Status(httptest.StatusOK).JSON().Equal(expectedOutput) - e.POST("/myHandler3").WithJSON(input).Expect().Status(httptest.StatusOK).JSON().Equal(expectedOutput) - e.GET("/myHandler4").Expect().Status(httptest.StatusOK).Body().Equal("MyHandler4") -} - -type testStructErrorHandler struct{} - -func (s *testStructErrorHandler) HandleError(ctx iris.Context, err error) { - ctx.StopWithError(httptest.StatusConflict, err) -} - -func (s *testStructErrorHandler) Handle(errText string) error { - return errors.New(errText) -} - -func TestStructErrorHandler(t *testing.T) { - b := New() - s := b.Struct(&testStructErrorHandler{}, 0) - - app := iris.New() - app.Get("/{errText:string}", s.MethodHandler("Handle", 0)) - - expectedErrText := "an error" - e := httptest.New(t, app) - e.GET("/" + expectedErrText).Expect().Status(httptest.StatusConflict).Body().Equal(expectedErrText) -} - -type ( - testServiceInterface1 interface { - Parse() string - } - - testServiceImpl1 struct { - inner string - } - - testServiceInterface2 interface { - } - - testServiceImpl2 struct { - tf int - } - - testControllerDependenciesSorter struct { - Service2 testServiceInterface2 - Service1 testServiceInterface1 - } -) - -func (s *testServiceImpl1) Parse() string { - return s.inner -} - -func (c *testControllerDependenciesSorter) Index() string { - return fmt.Sprintf("%#+v | %#+v", c.Service1, c.Service2) -} - -func TestStructFieldsSorter(t *testing.T) { // see https://github.com/kataras/iris/issues/1343 - b := New() - b.Register(&testServiceImpl1{"parser"}) - b.Register(&testServiceImpl2{24}) - s := b.Struct(&testControllerDependenciesSorter{}, 0) - - app := iris.New() - app.Get("/", s.MethodHandler("Index", 0)) - - e := httptest.New(t, app) - - expectedBody := `&hero_test.testServiceImpl1{inner:"parser"} | &hero_test.testServiceImpl2{tf:24}` - e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal(expectedBody) -} diff --git a/httptest/httptest.go b/httptest/httptest.go index 379e385e7a..30383a5342 100644 --- a/httptest/httptest.go +++ b/httptest/httptest.go @@ -6,9 +6,9 @@ import ( "net/http/httptest" "testing" - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/router" + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/router" "github.com/iris-contrib/httpexpect/v2" ) diff --git a/i18n/i18n.go b/i18n/i18n.go index 667d5c2ca6..75d59990ff 100644 --- a/i18n/i18n.go +++ b/i18n/i18n.go @@ -9,8 +9,8 @@ import ( "strings" "sync" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/router" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/router" "golang.org/x/text/language" ) diff --git a/i18n/loader.go b/i18n/loader.go index 5ef36934b9..501354d084 100644 --- a/i18n/loader.go +++ b/i18n/loader.go @@ -9,7 +9,7 @@ import ( "strings" "text/template" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" "github.com/BurntSushi/toml" "golang.org/x/text/language" diff --git a/iris.go b/iris.go index 5f84a3b4ee..2c835c3603 100644 --- a/iris.go +++ b/iris.go @@ -13,22 +13,37 @@ import ( "sync" "time" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/errgroup" - "github.com/kataras/iris/v12/core/host" - "github.com/kataras/iris/v12/core/netutil" - "github.com/kataras/iris/v12/core/router" - "github.com/kataras/iris/v12/i18n" - requestLogger "github.com/kataras/iris/v12/middleware/logger" - "github.com/kataras/iris/v12/middleware/recover" - "github.com/kataras/iris/v12/view" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/errgroup" + "github.com/kataras/iris/core/host" + "github.com/kataras/iris/core/netutil" + "github.com/kataras/iris/core/router" + "github.com/kataras/iris/i18n" + requestLogger "github.com/kataras/iris/middleware/logger" + "github.com/kataras/iris/middleware/recover" + "github.com/kataras/iris/view" "github.com/kataras/golog" "github.com/kataras/tunnel" ) // Version is the current version number of the Iris Web Framework. -const Version = "12.2.0" +const Version = "stale" + +func init() { + golog.Fatal(`You have installed an invalid version. Install with: +go get -u github.com/kataras/iris/v12@latest + +If your Open Source project depends on that pre-go1.9 version please open an issue +at https://github.com/kataras/iris/issues/new and share your repository with us, +we will upgrade your project's code base to the latest version for free. + +If you have a commercial project that you cannot share publically, please contact with +@kataras at https://chat.iris-go.com. Assistance will be provided to you and your colleagues +for free. +`) + +} // Byte unit helpers. const ( @@ -408,7 +423,7 @@ func (app *Application) Shutdown(ctx stdContext.Context) error { // If error occurred while building the Application, the returns type of error will be an *errgroup.Group // which let the callers to inspect the errors and cause, usage: // -// import "github.com/kataras/iris/v12/core/errgroup" +// import "github.com/kataras/iris/core/errgroup" // // errgroup.Walk(app.Build(), func(typ interface{}, err error) { // app.Logger().Errorf("%s: %s", typ, err) diff --git a/macro/handler/handler.go b/macro/handler/handler.go index 42016cada2..a8fe6a1fac 100644 --- a/macro/handler/handler.go +++ b/macro/handler/handler.go @@ -3,9 +3,9 @@ package handler import ( - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/memstore" - "github.com/kataras/iris/v12/macro" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/memstore" + "github.com/kataras/iris/macro" ) // CanMakeHandler reports whether a macro template needs a special macro's evaluator handler to be validated diff --git a/macro/handler/handler_test.go b/macro/handler/handler_test.go deleted file mode 100644 index f2fed9658e..0000000000 --- a/macro/handler/handler_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package handler - -import ( - "testing" - - "github.com/kataras/iris/v12/macro" -) - -func TestCanMakeHandler(t *testing.T) { - tests := []struct { - src string - needsHandler bool - }{ - {"/static/static", false}, - {"/{myparam}", false}, - {"/{myparam min(1)}", true}, - {"/{myparam else 500}", true}, - {"/{myparam else 404}", false}, - {"/{myparam:string}/static", false}, - {"/{myparam:int}", true}, - {"/static/{myparam:int}/static", true}, - {"/{myparam:path}", false}, - {"/{myparam:path min(1) else 404}", true}, - } - - availableMacros := *macro.Defaults - for i, tt := range tests { - tmpl, err := macro.Parse(tt.src, availableMacros) - if err != nil { - t.Fatalf("[%d] '%s' failed to be parsed: %v", i, tt.src, err) - } - - if got := CanMakeHandler(tmpl); got != tt.needsHandler { - if tt.needsHandler { - t.Fatalf("[%d] '%s' expected to be able to generate an evaluator handler instead of a nil one", i, tt.src) - } else { - t.Fatalf("[%d] '%s' should not need an evaluator handler", i, tt.src) - } - } - } -} diff --git a/macro/interpreter/lexer/lexer.go b/macro/interpreter/lexer/lexer.go index 76d91c4d85..7bd288b63d 100644 --- a/macro/interpreter/lexer/lexer.go +++ b/macro/interpreter/lexer/lexer.go @@ -1,7 +1,7 @@ package lexer import ( - "github.com/kataras/iris/v12/macro/interpreter/token" + "github.com/kataras/iris/macro/interpreter/token" ) // Lexer helps us to read/scan characters of a source and resolve their token types. diff --git a/macro/interpreter/lexer/lexer_test.go b/macro/interpreter/lexer/lexer_test.go deleted file mode 100644 index 6a6b860504..0000000000 --- a/macro/interpreter/lexer/lexer_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package lexer - -import ( - "testing" - - "github.com/kataras/iris/v12/macro/interpreter/token" -) - -func TestNextToken(t *testing.T) { - input := `{id:int min(1) max(5) else 404}` - - tests := []struct { - expectedType token.Type - expectedLiteral string - }{ - {token.LBRACE, "{"}, // 0 - {token.IDENT, "id"}, // 1 - {token.COLON, ":"}, // 2 - {token.IDENT, "int"}, // 3 - {token.IDENT, "min"}, // 4 - {token.LPAREN, "("}, // 5 - {token.INT, "1"}, // 6 - {token.RPAREN, ")"}, // 7 - {token.IDENT, "max"}, // 8 - {token.LPAREN, "("}, // 9 - {token.INT, "5"}, // 10 - {token.RPAREN, ")"}, // 11 - {token.ELSE, "else"}, // 12 - {token.INT, "404"}, // 13 - {token.RBRACE, "}"}, // 14 - } - - l := New(input) - - for i, tt := range tests { - tok := l.NextToken() - - if tok.Type != tt.expectedType { - t.Fatalf("tests[%d] - tokentype wrong. expected=%q, got=%q", - i, tt.expectedType, tok.Type) - } - - if tok.Literal != tt.expectedLiteral { - t.Fatalf("tests[%d] - literal wrong. expected=%q, got=%q", - i, tt.expectedLiteral, tok.Literal) - } - - } -} - -// EMEINA STO: -// 30/232 selida apto making a interpeter in Go. -// den ekana to skipWhitespaces giati skeftomai -// an borei na to xreiastw 9a dw aurio. diff --git a/macro/interpreter/parser/parser.go b/macro/interpreter/parser/parser.go index ab19918624..8c89202bb2 100644 --- a/macro/interpreter/parser/parser.go +++ b/macro/interpreter/parser/parser.go @@ -5,9 +5,9 @@ import ( "strconv" "strings" - "github.com/kataras/iris/v12/macro/interpreter/ast" - "github.com/kataras/iris/v12/macro/interpreter/lexer" - "github.com/kataras/iris/v12/macro/interpreter/token" + "github.com/kataras/iris/macro/interpreter/ast" + "github.com/kataras/iris/macro/interpreter/lexer" + "github.com/kataras/iris/macro/interpreter/token" ) // Parse takes a route "fullpath" diff --git a/macro/interpreter/parser/parser_test.go b/macro/interpreter/parser/parser_test.go deleted file mode 100644 index cfc152f800..0000000000 --- a/macro/interpreter/parser/parser_test.go +++ /dev/null @@ -1,399 +0,0 @@ -package parser - -import ( - "fmt" - "reflect" - "strings" - "testing" - - "github.com/kataras/iris/v12/macro/interpreter/ast" -) - -type simpleParamType string - -func (pt simpleParamType) Indent() string { return string(pt) } - -type masterParamType simpleParamType - -func (pt masterParamType) Indent() string { return string(pt) } -func (pt masterParamType) Master() bool { return true } - -type wildcardParamType string - -func (pt wildcardParamType) Indent() string { return string(pt) } -func (pt wildcardParamType) Trailing() bool { return true } - -type aliasedParamType []string - -func (pt aliasedParamType) Indent() string { return string(pt[0]) } -func (pt aliasedParamType) Alias() string { return pt[1] } - -var ( - paramTypeString = masterParamType("string") - paramTypeNumber = aliasedParamType{"number", "int"} - paramTypeInt64 = aliasedParamType{"int64", "long"} - paramTypeUint8 = simpleParamType("uint8") - paramTypeUint64 = simpleParamType("uint64") - paramTypeBool = aliasedParamType{"bool", "boolean"} - paramTypeAlphabetical = simpleParamType("alphabetical") - paramTypeFile = simpleParamType("file") - paramTypePath = wildcardParamType("path") -) - -var testParamTypes = []ast.ParamType{ - paramTypeString, - paramTypeNumber, paramTypeInt64, paramTypeUint8, paramTypeUint64, - paramTypeBool, - paramTypeAlphabetical, paramTypeFile, paramTypePath, -} - -func TestParseParamError(t *testing.T) { - // fail - illegalChar := '$' - - input := "{id" + string(illegalChar) + "int range(1,5) else 404}" - p := NewParamParser(input) - - _, err := p.Parse(testParamTypes) - - if err == nil { - t.Fatalf("expecting not empty error on input '%s'", input) - } - - illIdx := strings.IndexRune(input, illegalChar) - expectedErr := fmt.Sprintf("[%d:%d] illegal token: %s", illIdx, illIdx, "$") - if got := err.Error(); got != expectedErr { - t.Fatalf("expecting error to be '%s' but got: %s", expectedErr, got) - } - // - - // success - input2 := "{id:uint64 range(1,5) else 404}" - p.Reset(input2) - _, err = p.Parse(testParamTypes) - - if err != nil { - t.Fatalf("expecting empty error on input '%s', but got: %s", input2, err.Error()) - } - // -} - -// mustLookupParamType same as `ast.LookupParamType` but it panics if "indent" does not match with a valid Param Type. -func mustLookupParamType(indent string) ast.ParamType { - pt, found := ast.LookupParamType(indent, testParamTypes...) - if !found { - panic("param type '" + indent + "' is not part of the provided param types") - } - - return pt -} - -func TestParseParam(t *testing.T) { - tests := []struct { - valid bool - expectedStatement ast.ParamStatement - }{ - { - true, - ast.ParamStatement{ - Src: "{id:int min(1) max(5) else 404}", - Name: "id", - Type: mustLookupParamType("number"), - Funcs: []ast.ParamFunc{ - { - Name: "min", - Args: []string{"1"}, - }, - { - Name: "max", - Args: []string{"5"}, - }, - }, - ErrorCode: 404, - }, - }, // 0 - - { - true, - ast.ParamStatement{ - // test alias of int. - Src: "{id:number range(1,5)}", - Name: "id", - Type: mustLookupParamType("number"), - Funcs: []ast.ParamFunc{ - { - Name: "range", - Args: []string{"1", "5"}, - }, - }, - ErrorCode: 404, - }, - }, // 1 - { - true, - ast.ParamStatement{ - Src: "{file:path contains(.)}", - Name: "file", - Type: mustLookupParamType("path"), - Funcs: []ast.ParamFunc{ - { - Name: "contains", - Args: []string{"."}, - }, - }, - ErrorCode: 404, - }, - }, // 2 - { - true, - ast.ParamStatement{ - Src: "{username:alphabetical}", - Name: "username", - Type: mustLookupParamType("alphabetical"), - ErrorCode: 404, - }, - }, // 3 - { - true, - ast.ParamStatement{ - Src: "{myparam}", - Name: "myparam", - Type: mustLookupParamType("string"), - ErrorCode: 404, - }, - }, // 4 - { - false, - ast.ParamStatement{ - Src: "{myparam_:thisianunexpected}", - Name: "myparam_", - Type: nil, - ErrorCode: 404, - }, - }, // 5 - { - true, - ast.ParamStatement{ - Src: "{myparam2}", - Name: "myparam2", // we now allow integers to the parameter names. - Type: ast.GetMasterParamType(testParamTypes...), - ErrorCode: 404, - }, - }, // 6 - { - true, - ast.ParamStatement{ - Src: "{id:int even()}", // test param funcs without any arguments (LPAREN peek for RPAREN) - Name: "id", - Type: mustLookupParamType("number"), - Funcs: []ast.ParamFunc{ - { - Name: "even", - }, - }, - ErrorCode: 404, - }, - }, // 7 - { - true, - ast.ParamStatement{ - Src: "{id:int64 else 404}", - Name: "id", - Type: mustLookupParamType("int64"), - ErrorCode: 404, - }, - }, // 8 - { - true, - ast.ParamStatement{ - Src: "{id:long else 404}", // backwards-compatible test. - Name: "id", - Type: mustLookupParamType("int64"), - ErrorCode: 404, - }, - }, // 9 - { - true, - ast.ParamStatement{ - Src: "{id:long else 404}", - Name: "id", - Type: mustLookupParamType("int64"), // backwards-compatible test of LookupParamType. - ErrorCode: 404, - }, - }, // 10 - { - true, - ast.ParamStatement{ - Src: "{has:bool else 404}", - Name: "has", - Type: mustLookupParamType("bool"), - ErrorCode: 404, - }, - }, // 11 - { - true, - ast.ParamStatement{ - Src: "{has:boolean else 404}", // backwards-compatible test. - Name: "has", - Type: mustLookupParamType("bool"), - ErrorCode: 404, - }, - }, // 12 - - } - - p := new(ParamParser) - for i, tt := range tests { - p.Reset(tt.expectedStatement.Src) - resultStmt, err := p.Parse(testParamTypes) - - if tt.valid && err != nil { - t.Fatalf("tests[%d] - error %s", i, err.Error()) - } else if !tt.valid && err == nil { - t.Fatalf("tests[%d] - expected to be a failure", i) - } - - if resultStmt != nil { // is valid here - if !reflect.DeepEqual(tt.expectedStatement, *resultStmt) { - t.Fatalf("tests[%d] - wrong statement, expected and result differs. Details:\n%#v\n%#v", i, tt.expectedStatement, *resultStmt) - } - } - - } -} - -func TestParse(t *testing.T) { - tests := []struct { - path string - valid bool - expectedStatements []ast.ParamStatement - }{ - { - "/api/users/{id:int min(1) max(5) else 404}", true, - []ast.ParamStatement{ - { - Src: "{id:int min(1) max(5) else 404}", - Name: "id", - Type: paramTypeNumber, - Funcs: []ast.ParamFunc{ - { - Name: "min", - Args: []string{"1"}, - }, - { - Name: "max", - Args: []string{"5"}, - }, - }, - ErrorCode: 404, - }, - }, - }, // 0 - { - "/admin/{id:uint64 range(1,5)}", true, - []ast.ParamStatement{ - { - Src: "{id:uint64 range(1,5)}", - Name: "id", - Type: paramTypeUint64, - Funcs: []ast.ParamFunc{ - { - Name: "range", - Args: []string{"1", "5"}, - }, - }, - ErrorCode: 404, - }, - }, - }, // 1 - { - "/files/{file:path contains(.)}", true, - []ast.ParamStatement{ - { - Src: "{file:path contains(.)}", - Name: "file", - Type: paramTypePath, - Funcs: []ast.ParamFunc{ - { - Name: "contains", - Args: []string{"."}, - }, - }, - ErrorCode: 404, - }, - }, - }, // 2 - { - "/profile/{username:alphabetical}", true, - []ast.ParamStatement{ - { - Src: "{username:alphabetical}", - Name: "username", - Type: paramTypeAlphabetical, - ErrorCode: 404, - }, - }, - }, // 3 - { - "/something/here/{myparam}", true, - []ast.ParamStatement{ - { - Src: "{myparam}", - Name: "myparam", - Type: paramTypeString, - ErrorCode: 404, - }, - }, - }, // 4 - { - "/unexpected/{myparam_:thisianunexpected}", false, - []ast.ParamStatement{ - { - Src: "{myparam_:thisianunexpected}", - Name: "myparam_", - Type: nil, - ErrorCode: 404, - }, - }, - }, // 5 - { - "/p2/{myparam2}", true, - []ast.ParamStatement{ - { - Src: "{myparam2}", - Name: "myparam2", // we now allow integers to the parameter names. - Type: paramTypeString, - ErrorCode: 404, - }, - }, - }, // 6 - { - "/assets/{file:path}/invalid", false, // path should be in the end segment - []ast.ParamStatement{ - { - Src: "{file:path}", - Name: "file", - Type: paramTypePath, - ErrorCode: 404, - }, - }, - }, // 7 - } - for i, tt := range tests { - statements, err := Parse(tt.path, testParamTypes) - - if tt.valid && err != nil { - t.Fatalf("tests[%d] - error %s", i, err.Error()) - } else if !tt.valid && err == nil { - t.Fatalf("tests[%d] - expected to be a failure", i) - } - for j := range statements { - for l := range tt.expectedStatements { - if !reflect.DeepEqual(tt.expectedStatements[l], *statements[j]) { - t.Fatalf("tests[%d] - wrong statements, expected and result differs. Details:\n%#v\n%#v", i, tt.expectedStatements[l], *statements[j]) - } - } - } - - } -} diff --git a/macro/macro_test.go b/macro/macro_test.go deleted file mode 100644 index 123ea69f5d..0000000000 --- a/macro/macro_test.go +++ /dev/null @@ -1,471 +0,0 @@ -package macro - -import ( - "reflect" - "strconv" - "testing" -) - -// Most important tests to look: -// ../parser/parser_test.go -// ../lexer/lexer_test.go - -func TestGoodParamFunc(t *testing.T) { - good1 := func(min int, max int) func(string) bool { - return func(paramValue string) bool { - return true - } - } - - good2 := func(min uint64, max uint64) func(string) bool { - return func(paramValue string) bool { - return true - } - } - - notgood1 := func(min int, max int) bool { - return false - } - - if !goodParamFunc(reflect.TypeOf(good1)) { - t.Fatalf("expected good1 func to be good but it's not") - } - - if !goodParamFunc(reflect.TypeOf(good2)) { - t.Fatalf("expected good2 func to be good but it's not") - } - - if goodParamFunc(reflect.TypeOf(notgood1)) { - t.Fatalf("expected notgood1 func to be the worst") - } -} - -func TestGoodParamFuncName(t *testing.T) { - tests := []struct { - name string - good bool - }{ - {"range", true}, - {"_range", true}, - {"range_", true}, - {"r_ange", true}, - // numbers or other symbols are invalid. - {"range1", false}, - {"2range", false}, - {"r@nge", false}, - {"rang3", false}, - } - for i, tt := range tests { - isGood := goodParamFuncName(tt.name) - if tt.good && !isGood { - t.Fatalf("tests[%d] - expecting valid name but got invalid for name %s", i, tt.name) - } else if !tt.good && isGood { - t.Fatalf("tests[%d] - expecting invalid name but got valid for name %s", i, tt.name) - } - } -} - -func testEvaluatorRaw(t *testing.T, macroEvaluator *Macro, input string, expectedType reflect.Kind, pass bool, i int) { - if macroEvaluator.Evaluator == nil && pass { - return // if not evaluator defined then it should allow everything. - } - value, passed := macroEvaluator.Evaluator(input) - if pass != passed { - t.Fatalf("%s - tests[%d] - expecting[pass] %v but got %v", t.Name(), i, pass, passed) - } - - if !passed { - return - } - - if value == nil && expectedType != reflect.Invalid { - t.Fatalf("%s - tests[%d] - expecting[value] to not be nil", t.Name(), i) - } - - if v := reflect.ValueOf(value); v.Kind() != expectedType { - t.Fatalf("%s - tests[%d] - expecting[value.Kind] %v but got %v", t.Name(), i, expectedType, v.Kind()) - } -} - -func TestStringEvaluatorRaw(t *testing.T) { - tests := []struct { - pass bool - input string - }{ - {true, "astring"}, // 0 - {true, "astringwith_numb3rS_and_symbol$"}, // 1 - {true, "32321"}, // 2 - {true, "main.css"}, // 3 - {true, "/assets/main.css"}, // 4 - // false never - } // 0 - - for i, tt := range tests { - testEvaluatorRaw(t, String, tt.input, reflect.String, tt.pass, i) - } -} - -func TestIntEvaluatorRaw(t *testing.T) { - x64 := strconv.IntSize == 64 - - tests := []struct { - pass bool - input string - }{ - {false, "astring"}, // 0 - {false, "astringwith_numb3rS_and_symbol$"}, // 1 - {true, "32321"}, // 2 - {x64, "9223372036854775807" /* max int64 */}, // 3 - {x64, "-9223372036854775808" /* min int64 */}, // 4 - {false, "-18446744073709553213213213213213121615"}, // 5 - {false, "42 18446744073709551615"}, // 6 - {false, "--42"}, // 7 - {false, "+42"}, // 8 - {false, "main.css"}, // 9 - {false, "/assets/main.css"}, // 10 - } - - for i, tt := range tests { - testEvaluatorRaw(t, Int, tt.input, reflect.Int, tt.pass, i) - } -} - -func TestInt8EvaluatorRaw(t *testing.T) { - tests := []struct { - pass bool - input string - }{ - {false, "astring"}, // 0 - {false, "astringwith_numb3rS_and_symbol$"}, // 1 - {false, "32321"}, // 2 - {true, "127" /* max int8 */}, // 3 - {true, "-128" /* min int8 */}, // 4 - {false, "128"}, // 5 - {false, "-129"}, // 6 - {false, "-18446744073709553213213213213213121615"}, // 7 - {false, "42 18446744073709551615"}, // 8 - {false, "--42"}, // 9 - {false, "+42"}, // 10 - {false, "main.css"}, // 11 - {false, "/assets/main.css"}, // 12 - } - - for i, tt := range tests { - testEvaluatorRaw(t, Int8, tt.input, reflect.Int8, tt.pass, i) - } -} - -func TestInt16EvaluatorRaw(t *testing.T) { - tests := []struct { - pass bool - input string - }{ - {false, "astring"}, // 0 - {false, "astringwith_numb3rS_and_symbol$"}, // 1 - {true, "32321"}, // 2 - {true, "32767" /* max int16 */}, // 3 - {true, "-32768" /* min int16 */}, // 4 - {false, "-32769"}, // 5 - {false, "32768"}, // 6 - {false, "-18446744073709553213213213213213121615"}, // 7 - {false, "42 18446744073709551615"}, // 8 - {false, "--42"}, // 9 - {false, "+42"}, // 10 - {false, "main.css"}, // 11 - {false, "/assets/main.css"}, // 12 - } - - for i, tt := range tests { - testEvaluatorRaw(t, Int16, tt.input, reflect.Int16, tt.pass, i) - } -} - -func TestInt32EvaluatorRaw(t *testing.T) { - tests := []struct { - pass bool - input string - }{ - {false, "astring"}, // 0 - {false, "astringwith_numb3rS_and_symbol$"}, // 1 - {true, "32321"}, // 2 - {true, "1"}, // 3 - {true, "42"}, // 4 - {true, "2147483647" /* max int32 */}, // 5 - {true, "-2147483648" /* min int32 */}, // 6 - {false, "-2147483649"}, // 7 - {false, "2147483648"}, // 8 - {false, "-18446744073709553213213213213213121615"}, // 9 - {false, "42 18446744073709551615"}, // 10 - {false, "--42"}, // 11 - {false, "+42"}, // 12 - {false, "main.css"}, // 13 - {false, "/assets/main.css"}, // 14 - } - - for i, tt := range tests { - testEvaluatorRaw(t, Int32, tt.input, reflect.Int32, tt.pass, i) - } -} - -func TestInt64EvaluatorRaw(t *testing.T) { - tests := []struct { - pass bool - input string - }{ - {false, "astring"}, // 0 - {false, "astringwith_numb3rS_and_symbol$"}, // 1 - {false, "18446744073709551615"}, // 2 - {false, "92233720368547758079223372036854775807"}, // 3 - {false, "9223372036854775808 9223372036854775808"}, // 4 - {false, "main.css"}, // 5 - {false, "/assets/main.css"}, // 6 - {true, "9223372036854775807"}, // 7 - {true, "-9223372036854775808"}, // 8 - {true, "-0"}, // 9 - {true, "1"}, // 10 - {true, "-042"}, // 11 - {true, "142"}, // 12 - } - - for i, tt := range tests { - testEvaluatorRaw(t, Int64, tt.input, reflect.Int64, tt.pass, i) - } -} - -func TestUintEvaluatorRaw(t *testing.T) { - x64 := strconv.IntSize == 64 - - tests := []struct { - pass bool - input string - }{ - {false, "astring"}, // 0 - {false, "astringwith_numb3rS_and_symbol$"}, // 1 - {true, "32321"}, // 2 - {true, "1"}, // 3 - {true, "42"}, // 4 - {x64, "18446744073709551615" /* max uint64 */}, // 5 - {true, "4294967295" /* max uint32 */}, // 6 - {false, "-2147483649"}, // 7 - {true, "2147483648"}, // 8 - {false, "-18446744073709553213213213213213121615"}, // 9 - {false, "42 18446744073709551615"}, // 10 - {false, "--42"}, // 11 - {false, "+42"}, // 12 - {false, "main.css"}, // 13 - {false, "/assets/main.css"}, // 14 - } - - for i, tt := range tests { - testEvaluatorRaw(t, Uint, tt.input, reflect.Uint, tt.pass, i) - } -} - -func TestUint8EvaluatorRaw(t *testing.T) { - tests := []struct { - pass bool - input string - }{ - {false, "astring"}, // 0 - {false, "astringwith_numb3rS_and_symbol$"}, // 1 - {false, "-9223372036854775808"}, // 2 - {false, "main.css"}, // 3 - {false, "/assets/main.css"}, // 4 - {false, "92233720368547758079223372036854775807"}, // 5 - {false, "9223372036854775808 9223372036854775808"}, // 6 - {false, "-1"}, // 7 - {false, "-0"}, // 8 - {false, "+1"}, // 9 - {false, "18446744073709551615"}, // 10 - {false, "9223372036854775807"}, // 11 - {false, "021"}, // 12 - no leading zeroes are allowed. - {false, "300"}, // 13 - {true, "0"}, // 14 - {true, "255"}, // 15 - {true, "21"}, // 16 - } - - for i, tt := range tests { - testEvaluatorRaw(t, Uint8, tt.input, reflect.Uint8, tt.pass, i) - } -} - -func TestUint16EvaluatorRaw(t *testing.T) { - tests := []struct { - pass bool - input string - }{ - {false, "astring"}, // 0 - {false, "astringwith_numb3rS_and_symbol$"}, // 1 - {true, "32321"}, // 2 - {true, "65535" /* max uint16 */}, // 3 - {true, "0" /* min uint16 */}, // 4 - {false, "-32769"}, // 5 - {true, "32768"}, // 6 - {false, "-18446744073709553213213213213213121615"}, // 7 - {false, "42 18446744073709551615"}, // 8 - {false, "--42"}, // 9 - {false, "+42"}, // 10 - {false, "main.css"}, // 11 - {false, "/assets/main.css"}, // 12 - } - - for i, tt := range tests { - testEvaluatorRaw(t, Uint16, tt.input, reflect.Uint16, tt.pass, i) - } -} - -func TestUint32EvaluatorRaw(t *testing.T) { - tests := []struct { - pass bool - input string - }{ - {false, "astring"}, // 0 - {false, "astringwith_numb3rS_and_symbol$"}, // 1 - {true, "32321"}, // 2 - {true, "1"}, // 3 - {true, "42"}, // 4 - {true, "4294967295" /* max uint32*/}, // 5 - {true, "0" /* min uint32 */}, // 6 - {false, "-2147483649"}, // 7 - {true, "2147483648"}, // 8 - {false, "-18446744073709553213213213213213121615"}, // 9 - {false, "42 18446744073709551615"}, // 10 - {false, "--42"}, // 11 - {false, "+42"}, // 12 - {false, "main.css"}, // 13 - {false, "/assets/main.css"}, // 14 - } - - for i, tt := range tests { - testEvaluatorRaw(t, Uint32, tt.input, reflect.Uint32, tt.pass, i) - } -} - -func TestUint64EvaluatorRaw(t *testing.T) { - tests := []struct { - pass bool - input string - }{ - {false, "astring"}, // 0 - {false, "astringwith_numb3rS_and_symbol$"}, // 1 - {false, "-9223372036854775808"}, // 2 - {false, "main.css"}, // 3 - {false, "/assets/main.css"}, // 4 - {false, "92233720368547758079223372036854775807"}, // 5 - {false, "9223372036854775808 9223372036854775808"}, // 6 - {false, "-1"}, // 7 - {false, "-0"}, // 8 - {false, "+1"}, // 9 - {true, "18446744073709551615"}, // 10 - {true, "9223372036854775807"}, // 11 - {true, "0"}, // 12 - } - - for i, tt := range tests { - testEvaluatorRaw(t, Uint64, tt.input, reflect.Uint64, tt.pass, i) - } -} - -func TestAlphabeticalEvaluatorRaw(t *testing.T) { - tests := []struct { - pass bool - input string - }{ - {true, "astring"}, // 0 - {false, "astringwith_numb3rS_and_symbol$"}, // 1 - {false, "32321"}, // 2 - {false, "main.css"}, // 3 - {false, "/assets/main.css"}, // 4 - } - - for i, tt := range tests { - testEvaluatorRaw(t, Alphabetical, tt.input, reflect.String, tt.pass, i) - } -} - -func TestFileEvaluatorRaw(t *testing.T) { - tests := []struct { - pass bool - input string - }{ - {true, "astring"}, // 0 - {false, "astringwith_numb3rS_and_symbol$"}, // 1 - {true, "32321"}, // 2 - {true, "main.css"}, // 3 - {false, "/assets/main.css"}, // 4 - } - - for i, tt := range tests { - testEvaluatorRaw(t, File, tt.input, reflect.String, tt.pass, i) - } -} - -func TestPathEvaluatorRaw(t *testing.T) { - pathTests := []struct { - pass bool - input string - }{ - {true, "astring"}, // 0 - {true, "astringwith_numb3rS_and_symbol$"}, // 1 - {true, "32321"}, // 2 - {true, "main.css"}, // 3 - {true, "/assets/main.css"}, // 4 - {true, "disk/assets/main.css"}, // 5 - } - - for i, tt := range pathTests { - testEvaluatorRaw(t, Path, tt.input, reflect.String, tt.pass, i) - } -} - -func TestConvertBuilderFunc(t *testing.T) { - fn := func(min uint64, slice []string) func(string) bool { - return func(paramValue string) bool { - if expected, got := "ok", paramValue; expected != got { - t.Fatalf("paramValue is not the expected one: %s vs %s", expected, got) - } - - if expected, got := uint64(1), min; expected != got { - t.Fatalf("min argument is not the expected one: %d vs %d", expected, got) - } - - if expected, got := []string{"name1", "name2"}, slice; len(expected) == len(got) { - if expected, got := "name1", slice[0]; expected != got { - t.Fatalf("slice argument[%d] does not contain the expected value: %s vs %s", 0, expected, got) - } - - if expected, got := "name2", slice[1]; expected != got { - t.Fatalf("slice argument[%d] does not contain the expected value: %s vs %s", 1, expected, got) - } - } else { - t.Fatalf("slice argument is not the expected one, the length is difference: %d vs %d", len(expected), len(got)) - } - - return true - } - } - - evalFunc := convertBuilderFunc(fn) - if !evalFunc([]string{"1", "[name1,name2]"}).Call([]reflect.Value{reflect.ValueOf("ok")})[0].Interface().(bool) { - t.Fatalf("failed, it should fail already") - } - - fnSimplify := func(requestParamValue string) bool { - return requestParamValue == "kataras" - } - - evalFunc = convertBuilderFunc(fnSimplify) - if !evalFunc([]string{}).Call([]reflect.Value{reflect.ValueOf("kataras")})[0].Interface().(bool) { - t.Fatalf("it should pass, the combile arguments are empty and the given request value is the expected one") - } - - defer func() { - if r := recover(); r == nil { - t.Fatalf("it should panic, the combile arguments are more than one") - } - }() - - // should panic. - evalFunc([]string{"1"}).Call([]reflect.Value{reflect.ValueOf("kataras")}) -} diff --git a/macro/macros.go b/macro/macros.go index f88aca069b..9963718203 100644 --- a/macro/macros.go +++ b/macro/macros.go @@ -4,7 +4,7 @@ import ( "strconv" "strings" - "github.com/kataras/iris/v12/macro/interpreter/ast" + "github.com/kataras/iris/macro/interpreter/ast" ) var ( diff --git a/macro/template.go b/macro/template.go index 845646fc9c..d0bdd238e7 100644 --- a/macro/template.go +++ b/macro/template.go @@ -3,8 +3,8 @@ package macro import ( "reflect" - "github.com/kataras/iris/v12/macro/interpreter/ast" - "github.com/kataras/iris/v12/macro/interpreter/parser" + "github.com/kataras/iris/macro/interpreter/ast" + "github.com/kataras/iris/macro/interpreter/parser" ) // Template contains a route's path full parsed template. diff --git a/middleware/basicauth/basicauth.go b/middleware/basicauth/basicauth.go index 79831dfb72..5c0c88b15a 100644 --- a/middleware/basicauth/basicauth.go +++ b/middleware/basicauth/basicauth.go @@ -8,8 +8,8 @@ import ( "strconv" "time" - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris" + "github.com/kataras/iris/context" ) func init() { diff --git a/middleware/basicauth/config.go b/middleware/basicauth/config.go index 3b885b14f0..84467c434d 100644 --- a/middleware/basicauth/config.go +++ b/middleware/basicauth/config.go @@ -3,7 +3,7 @@ package basicauth import ( "time" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) const ( diff --git a/middleware/grpc/grpc.go b/middleware/grpc/grpc.go index b073fdf1ea..4600679145 100644 --- a/middleware/grpc/grpc.go +++ b/middleware/grpc/grpc.go @@ -4,7 +4,7 @@ import ( "net/http" "strings" - "github.com/kataras/iris/v12/core/router" + "github.com/kataras/iris/core/router" ) // New returns a new gRPC Iris router wrapper for a gRPC server. @@ -13,7 +13,7 @@ import ( // The Iris server SHOULD run under HTTP/2 and clients too. // // Usage: -// import grpcWrapper "github.com/kataras/iris/v12/middleware/grpc" +// import grpcWrapper "github.com/kataras/iris/middleware/grpc" // [...] // app := iris.New() // grpcServer := grpc.NewServer() diff --git a/middleware/hcaptcha/hcaptcha.go b/middleware/hcaptcha/hcaptcha.go index e6cb41a360..0a330bd35b 100644 --- a/middleware/hcaptcha/hcaptcha.go +++ b/middleware/hcaptcha/hcaptcha.go @@ -7,7 +7,7 @@ import ( "net/http" "net/url" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) func init() { diff --git a/middleware/jwt/jwt.go b/middleware/jwt/jwt.go index 00df66633e..411d8eadf6 100644 --- a/middleware/jwt/jwt.go +++ b/middleware/jwt/jwt.go @@ -8,7 +8,7 @@ import ( "strings" "time" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" "github.com/square/go-jose/v3" "github.com/square/go-jose/v3/jwt" diff --git a/middleware/jwt/jwt_test.go b/middleware/jwt/jwt_test.go deleted file mode 100644 index 0d30d6fd48..0000000000 --- a/middleware/jwt/jwt_test.go +++ /dev/null @@ -1,139 +0,0 @@ -// Package jwt_test contains simple Iris jwt tests. Most of the jwt functionality is already tested inside the jose package itself. -package jwt_test - -import ( - "os" - "testing" - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/httptest" - "github.com/kataras/iris/v12/middleware/jwt" -) - -type userClaims struct { - jwt.Claims - Username string -} - -const testMaxAge = 3 * time.Second - -// Random RSA verification and encryption. -func TestRSA(t *testing.T) { - j := jwt.RSA(testMaxAge) - t.Cleanup(func() { - os.Remove(jwt.DefaultSignFilename) - os.Remove(jwt.DefaultEncFilename) - }) - testWriteVerifyToken(t, j) -} - -// HMAC verification and encryption. -func TestHMAC(t *testing.T) { - j := jwt.HMAC(testMaxAge, "secret", "itsa16bytesecret") - testWriteVerifyToken(t, j) -} - -func TestNew_HMAC(t *testing.T) { - j, err := jwt.New(testMaxAge, jwt.HS256, []byte("secret")) - if err != nil { - t.Fatal(err) - } - err = j.WithEncryption(jwt.A128GCM, jwt.DIRECT, []byte("itsa16bytesecret")) - if err != nil { - t.Fatal(err) - } - - testWriteVerifyToken(t, j) -} - -// HMAC verification only (unecrypted). -func TestVerify(t *testing.T) { - j, err := jwt.New(testMaxAge, jwt.HS256, []byte("another secret")) - if err != nil { - t.Fatal(err) - } - testWriteVerifyToken(t, j) -} - -func testWriteVerifyToken(t *testing.T, j *jwt.JWT) { - t.Helper() - - j.Extractors = append(j.Extractors, jwt.FromJSON("access_token")) - standardClaims := jwt.Claims{Issuer: "an-issuer", Audience: jwt.Audience{"an-audience"}} - expectedClaims := userClaims{ - Claims: j.Expiry(standardClaims), - Username: "kataras", - } - - app := iris.New() - app.Get("/auth", func(ctx iris.Context) { - j.WriteToken(ctx, expectedClaims) - }) - - app.Post("/restricted", func(ctx iris.Context) { - var claims userClaims - if err := j.VerifyToken(ctx, &claims); err != nil { - ctx.StopWithStatus(iris.StatusUnauthorized) - return - } - - ctx.JSON(claims) - }) - - app.Post("/restricted_middleware_readclaims", j.Verify, func(ctx iris.Context) { - var claims userClaims - if err := jwt.ReadClaims(ctx, &claims); err != nil { - ctx.StopWithStatus(iris.StatusUnauthorized) - return - } - - ctx.JSON(claims) - }) - - app.Post("/restricted_middleware_get", j.Verify, func(ctx iris.Context) { - claims, err := jwt.Get(ctx) - if err != nil { - ctx.StopWithStatus(iris.StatusUnauthorized) - return - } - - ctx.JSON(claims) - }) - - e := httptest.New(t, app) - - // Get token. - rawToken := e.GET("/auth").Expect().Status(httptest.StatusOK).Body().Raw() - if rawToken == "" { - t.Fatalf("empty token") - } - - restrictedPaths := [...]string{"/restricted", "/restricted_middleware_readclaims", "/restricted_middleware_get"} - - now := time.Now() - for _, path := range restrictedPaths { - // Authorization Header. - e.POST(path).WithHeader("Authorization", "Bearer "+rawToken).Expect(). - Status(httptest.StatusOK).JSON().Equal(expectedClaims) - - // URL Query. - e.POST(path).WithQuery("token", rawToken).Expect(). - Status(httptest.StatusOK).JSON().Equal(expectedClaims) - - // JSON Body. - e.POST(path).WithJSON(iris.Map{"access_token": rawToken}).Expect(). - Status(httptest.StatusOK).JSON().Equal(expectedClaims) - - // Missing "Bearer". - e.POST(path).WithHeader("Authorization", rawToken).Expect(). - Status(httptest.StatusUnauthorized) - } - expireRemDur := testMaxAge - time.Since(now) - - // Expiration. - time.Sleep(expireRemDur /* -end */) - for _, path := range restrictedPaths { - e.POST(path).WithQuery("token", rawToken).Expect().Status(httptest.StatusUnauthorized) - } -} diff --git a/middleware/logger/config.go b/middleware/logger/config.go index cf6ef60173..138037cf45 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -3,7 +3,7 @@ package logger import ( "time" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) // The SkipperFunc signature, used to serve the main request without logs. diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index af61b639be..164d79a5d2 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -6,7 +6,7 @@ import ( "strconv" "time" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" "github.com/ryanuber/columnize" ) diff --git a/middleware/methodoverride/methodoverride.go b/middleware/methodoverride/methodoverride.go index d6c2fbce99..6a8914179a 100644 --- a/middleware/methodoverride/methodoverride.go +++ b/middleware/methodoverride/methodoverride.go @@ -5,8 +5,8 @@ import ( "net/http" "strings" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/router" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/router" ) type options struct { diff --git a/middleware/methodoverride/methodoverride_test.go b/middleware/methodoverride/methodoverride_test.go deleted file mode 100644 index fe6ec92dea..0000000000 --- a/middleware/methodoverride/methodoverride_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package methodoverride_test - -import ( - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/httptest" - "github.com/kataras/iris/v12/middleware/methodoverride" -) - -func TestMethodOverrideWrapper(t *testing.T) { - app := iris.New() - - mo := methodoverride.New( - // Defaults to nil. - // - methodoverride.SaveOriginalMethod("_originalMethod"), - // Default values. - // - // methodoverride.Methods(http.MethodPost), - // methodoverride.Headers("X-HTTP-Method", "X-HTTP-Method-Override", "X-Method-Override"), - // methodoverride.FormField("_method"), - // methodoverride.Query("_method"), - ) - // Register it with `WrapRouter`. - app.WrapRouter(mo) - - var ( - expectedDelResponse = "delete resp" - expectedPostResponse = "post resp" - ) - - app.Post("/path", func(ctx iris.Context) { - ctx.WriteString(expectedPostResponse) - }) - - app.Delete("/path", func(ctx iris.Context) { - ctx.WriteString(expectedDelResponse) - }) - - app.Delete("/path2", func(ctx iris.Context) { - _, err := ctx.Writef("%s%s", expectedDelResponse, ctx.Request().Context().Value("_originalMethod")) - if err != nil { - t.Fatal(err) - } - }) - - e := httptest.New(t, app) - - // Test headers. - e.POST("/path").WithHeader("X-HTTP-Method", iris.MethodDelete).Expect(). - Status(iris.StatusOK).Body().Equal(expectedDelResponse) - e.POST("/path").WithHeader("X-HTTP-Method-Override", iris.MethodDelete).Expect(). - Status(iris.StatusOK).Body().Equal(expectedDelResponse) - e.POST("/path").WithHeader("X-Method-Override", iris.MethodDelete).Expect(). - Status(iris.StatusOK).Body().Equal(expectedDelResponse) - - // Test form field value. - e.POST("/path").WithFormField("_method", iris.MethodDelete).Expect(). - Status(iris.StatusOK).Body().Equal(expectedDelResponse) - - // Test URL Query (although it's the same as form field in this case). - e.POST("/path").WithQuery("_method", iris.MethodDelete).Expect(). - Status(iris.StatusOK).Body().Equal(expectedDelResponse) - - // Test saved original method and - // Test without registered "POST" route. - e.POST("/path2").WithQuery("_method", iris.MethodDelete).Expect(). - Status(iris.StatusOK).Body().Equal(expectedDelResponse + iris.MethodPost) - - // Test simple POST request without method override fields. - e.POST("/path").Expect().Status(iris.StatusOK).Body().Equal(expectedPostResponse) - - // Test simple DELETE request. - e.DELETE("/path").Expect().Status(iris.StatusOK).Body().Equal(expectedDelResponse) -} diff --git a/middleware/pprof/pprof.go b/middleware/pprof/pprof.go index a71b2b1004..9496b4b32f 100644 --- a/middleware/pprof/pprof.go +++ b/middleware/pprof/pprof.go @@ -7,8 +7,8 @@ import ( rpprof "runtime/pprof" "strings" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/handlerconv" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/handlerconv" ) func init() { diff --git a/middleware/rate/rate.go b/middleware/rate/rate.go index 1dedb8f0f5..d595b9efc0 100644 --- a/middleware/rate/rate.go +++ b/middleware/rate/rate.go @@ -7,7 +7,7 @@ import ( "sync" "time" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" "golang.org/x/time/rate" ) diff --git a/middleware/recaptcha/recaptcha.go b/middleware/recaptcha/recaptcha.go index 291e099540..ca28c060a6 100644 --- a/middleware/recaptcha/recaptcha.go +++ b/middleware/recaptcha/recaptcha.go @@ -7,8 +7,8 @@ import ( "net/url" "time" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/netutil" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/netutil" ) func init() { diff --git a/middleware/recover/recover.go b/middleware/recover/recover.go index 9208093fba..4b9d96bea8 100644 --- a/middleware/recover/recover.go +++ b/middleware/recover/recover.go @@ -6,7 +6,7 @@ import ( "runtime" "strconv" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) func init() { diff --git a/middleware/requestid/requestid.go b/middleware/requestid/requestid.go index 9c24245450..e14dcb522f 100644 --- a/middleware/requestid/requestid.go +++ b/middleware/requestid/requestid.go @@ -5,7 +5,7 @@ import ( "encoding/hex" "net/http/httputil" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" "github.com/google/uuid" ) diff --git a/middleware/requestid/requestid_test.go b/middleware/requestid/requestid_test.go deleted file mode 100644 index a5478d4b24..0000000000 --- a/middleware/requestid/requestid_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package requestid_test - -import ( - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/httptest" - "github.com/kataras/iris/v12/middleware/requestid" -) - -func TestRequestID(t *testing.T) { - app := iris.New() - h := func(ctx iris.Context) { - ctx.WriteString(requestid.Get(ctx)) - } - - def := app.Party("/default") - { - def.Use(requestid.New()) - def.Get("/", h) - } - - const expectedCustomID = "my_id" - custom := app.Party("/custom") - { - customGen := func(ctx *context.Context) string { - return expectedCustomID - } - - custom.Use(requestid.New(customGen)) - custom.Get("/", h) - } - - const expectedErrMsg = "no id" - customWithErr := app.Party("/custom_err") - { - customGen := func(ctx *context.Context) string { - ctx.StopWithText(iris.StatusUnauthorized, expectedErrMsg) - return "" - } - - customWithErr.Use(requestid.New(customGen)) - customWithErr.Get("/", h) - } - - const expectedCustomIDFromOtherMiddleware = "my custom id" - changeID := app.Party("/custom_change_id") - { - changeID.Use(func(ctx iris.Context) { - ctx.SetID(expectedCustomIDFromOtherMiddleware) - ctx.Next() - }) - changeID.Use(requestid.New()) - changeID.Get("/", h) - } - - e := httptest.New(t, app) - e.GET("/default").Expect().Status(httptest.StatusOK).Body().NotEmpty() - e.GET("/custom").Expect().Status(httptest.StatusOK).Body().Equal(expectedCustomID) - e.GET("/custom_err").Expect().Status(httptest.StatusUnauthorized).Body().Equal(expectedErrMsg) - e.GET("/custom_change_id").Expect().Status(httptest.StatusOK).Body().Equal(expectedCustomIDFromOtherMiddleware) -} diff --git a/mvc/aliases.go b/mvc/aliases.go index b8f87c8600..911f2d6f4d 100644 --- a/mvc/aliases.go +++ b/mvc/aliases.go @@ -1,8 +1,8 @@ package mvc import ( - "github.com/kataras/iris/v12/hero" - "github.com/kataras/iris/v12/versioning" + "github.com/kataras/iris/hero" + "github.com/kataras/iris/versioning" ) type ( diff --git a/mvc/controller.go b/mvc/controller.go index 939a6dcb63..cf9f1ec7e2 100644 --- a/mvc/controller.go +++ b/mvc/controller.go @@ -5,10 +5,10 @@ import ( "reflect" "strings" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/router" - "github.com/kataras/iris/v12/hero" - "github.com/kataras/iris/v12/macro" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/router" + "github.com/kataras/iris/hero" + "github.com/kataras/iris/macro" ) // BaseController is the optional controller interface, if it's diff --git a/mvc/controller_handle_test.go b/mvc/controller_handle_test.go deleted file mode 100644 index 59038b4239..0000000000 --- a/mvc/controller_handle_test.go +++ /dev/null @@ -1,201 +0,0 @@ -package mvc_test - -import ( - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/httptest" - - . "github.com/kataras/iris/v12/mvc" -) - -// service -type ( - // these testService and testServiceImpl could be in lowercase, unexported - // but the `Say` method should be exported however we have those exported - // because of the controller handler test. - testService interface { - Say(string) string - } - testServiceImpl struct { - prefix string - } -) - -func (s *testServiceImpl) Say(message string) string { - return s.prefix + " " + message -} - -type testControllerHandle struct { - Ctx *context.Context - Service testService - - reqField string -} - -func (c *testControllerHandle) BeforeActivation(b BeforeActivation) { - b.Handle("GET", "/histatic", "HiStatic") - b.Handle("GET", "/hiservice", "HiService") - b.Handle("GET", "/hiservice/{ps:string}", "HiServiceBy") - b.Handle("GET", "/hiparam/{ps:string}", "HiParamBy") - b.Handle("GET", "/hiparamempyinput/{ps:string}", "HiParamEmptyInputBy") - b.HandleMany("GET", "/custom/{ps:string} /custom2/{ps:string}", "CustomWithParameter") - b.HandleMany("GET", "/custom3/{ps:string}/{pssecond:string}", "CustomWithParameters") -} - -// test `GetRoute` for custom routes. -func (c *testControllerHandle) AfterActivation(a AfterActivation) { - // change automatic parser's route change name. - rget := a.GetRoute("Get") - if rget == nil { - panic("route from function name: 'Get' doesn't exist on `AfterActivation`") - } - rget.Name = "index_route" - - // change a custom route's name. - r := a.GetRoute("HiStatic") - if r == nil { - panic("route from function name: HiStatic doesn't exist on `AfterActivation`") - } - // change the name here, and test if name changed in the handler. - r.Name = "hi_static_route" -} - -func (c *testControllerHandle) BeginRequest(ctx iris.Context) { - c.reqField = ctx.URLParam("reqfield") -} - -func (c *testControllerHandle) EndRequest(ctx iris.Context) {} - -func (c *testControllerHandle) Get() string { - if c.Ctx.GetCurrentRoute().Name() != "index_route" { - return "Get's route's name didn't change on AfterActivation" - } - return "index" -} - -func (c *testControllerHandle) HiStatic() string { - if c.Ctx.GetCurrentRoute().Name() != "hi_static_route" { - return "HiStatic's route's name didn't change on AfterActivation" - } - - return c.reqField -} - -func (c *testControllerHandle) HiService() string { - return c.Service.Say("hi") -} - -func (c *testControllerHandle) HiServiceBy(v string) string { - return c.Service.Say("hi with param: " + v) -} - -func (c *testControllerHandle) HiParamBy(v string) string { - return v -} - -func (c *testControllerHandle) HiParamEmptyInputBy() string { - return "empty in but served with ctx.Params.Get('ps')=" + c.Ctx.Params().Get("ps") -} - -func (c *testControllerHandle) CustomWithParameter(param1 string) string { - return param1 -} - -func (c *testControllerHandle) CustomWithParameters(param1, param2 string) string { - return param1 + param2 -} - -type testSmallController struct{} - -// test ctx + id in the same time. -func (c *testSmallController) GetHiParamEmptyInputWithCtxBy(ctx *context.Context, id string) string { - return "empty in but served with ctx.Params.Get('param2')= " + ctx.Params().Get("param2") + " == id == " + id -} - -func TestControllerHandle(t *testing.T) { - app := iris.New() - m := New(app) - m.Register(&testServiceImpl{prefix: "service:"}) - m.Handle(new(testControllerHandle)) - m.Handle(new(testSmallController)) - - e := httptest.New(t, app) - - // test the index, is not part of the current package's implementation but do it. - e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal("index") - - // the important things now. - - // this test ensures that the BeginRequest of the controller will be - // called correctly and also the controller is binded to the first input argument - // (which is the function's receiver, if any, in this case the *testController in go). - expectedReqField := "this is a request field filled by this url param" - e.GET("/histatic").WithQuery("reqfield", expectedReqField).Expect().Status(httptest.StatusOK). - Body().Equal(expectedReqField) - // this test makes sure that the binded values of the controller is handled correctly - // and can be used in a user-defined, dynamic "mvc handler". - e.GET("/hiservice").Expect().Status(httptest.StatusOK). - Body().Equal("service: hi") - e.GET("/hiservice/value").Expect().Status(httptest.StatusOK). - Body().Equal("service: hi with param: value") - // this worked with a temporary variadic on the resolvemethodfunc which is not - // correct design, I should split the path and params with the rest of implementation - // in order a simple template.Src can be given. - e.GET("/hiparam/value").Expect().Status(httptest.StatusOK). - Body().Equal("value") - e.GET("/hiparamempyinput/value").Expect().Status(httptest.StatusOK). - Body().Equal("empty in but served with ctx.Params.Get('ps')=value") - e.GET("/custom/value1").Expect().Status(httptest.StatusOK). - Body().Equal("value1") - e.GET("/custom2/value2").Expect().Status(httptest.StatusOK). - Body().Equal("value2") - e.GET("/custom3/value1/value2").Expect().Status(httptest.StatusOK). - Body().Equal("value1value2") - e.GET("/custom3/value1").Expect().Status(httptest.StatusNotFound) - - e.GET("/hi/param/empty/input/with/ctx/value").Expect().Status(httptest.StatusOK). - Body().Equal("empty in but served with ctx.Params.Get('param2')= value == id == value") -} - -type testControllerHandleWithDynamicPathPrefix struct { - Ctx iris.Context -} - -func (c *testControllerHandleWithDynamicPathPrefix) GetBy(id string) string { - params := c.Ctx.Params() - return params.Get("model") + params.Get("action") + id -} - -func TestControllerHandleWithDynamicPathPrefix(t *testing.T) { - app := iris.New() - New(app.Party("/api/data/{model:string}/{action:string}")).Handle(new(testControllerHandleWithDynamicPathPrefix)) - e := httptest.New(t, app) - e.GET("/api/data/mymodel/myaction/myid").Expect().Status(httptest.StatusOK). - Body().Equal("mymodelmyactionmyid") -} - -type testControllerGetBy struct{} - -func (c *testControllerGetBy) GetBy(age int64) *testCustomStruct { - return &testCustomStruct{ - Age: int(age), - Name: "name", - } -} - -func TestControllerGetByWithAllowMethods(t *testing.T) { - app := iris.New() - app.Configure(iris.WithFireMethodNotAllowed) - // ^ this 405 status will not be fired on POST: project/... because of - // .AllowMethods, but it will on PUT. - - New(app.Party("/project").AllowMethods(iris.MethodGet, iris.MethodPost)).Handle(new(testControllerGetBy)) - - e := httptest.New(t, app) - e.GET("/project/42").Expect().Status(httptest.StatusOK). - JSON().Equal(&testCustomStruct{Age: 42, Name: "name"}) - e.POST("/project/42").Expect().Status(httptest.StatusOK) - e.PUT("/project/42").Expect().Status(httptest.StatusMethodNotAllowed) -} diff --git a/mvc/controller_method_parser.go b/mvc/controller_method_parser.go index c5fcb7ba02..70137a4ca1 100644 --- a/mvc/controller_method_parser.go +++ b/mvc/controller_method_parser.go @@ -8,8 +8,8 @@ import ( "strings" "unicode" - "github.com/kataras/iris/v12/core/router" - "github.com/kataras/iris/v12/macro" + "github.com/kataras/iris/core/router" + "github.com/kataras/iris/macro" ) const ( diff --git a/mvc/controller_method_result_test.go b/mvc/controller_method_result_test.go deleted file mode 100644 index 163bf1b11e..0000000000 --- a/mvc/controller_method_result_test.go +++ /dev/null @@ -1,271 +0,0 @@ -package mvc_test - -import ( - "errors" - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/httptest" - - . "github.com/kataras/iris/v12/mvc" -) - -type testControllerMethodResult struct { - Ctx *context.Context -} - -func (c *testControllerMethodResult) Get() Result { - return Response{ - Text: "Hello World!", - } -} - -func (c *testControllerMethodResult) GetWithStatus() Response { // or Result again, no problem. - return Response{ - Text: "This page doesn't exist", - Code: iris.StatusNotFound, - } -} - -type testCustomStruct struct { - Name string `json:"name" xml:"name"` - Age int `json:"age" xml:"age"` -} - -func (c *testControllerMethodResult) GetJson() Result { - var err error - if c.Ctx.URLParamExists("err") { - err = errors.New("error here") - } - return Response{ - Err: err, // if err != nil then it will fire the error's text with a BadRequest. - Object: testCustomStruct{Name: "Iris", Age: 2}, - } -} - -var things = []string{"thing 0", "thing 1", "thing 2"} - -func (c *testControllerMethodResult) GetThingWithTryBy(index int) Result { - failure := Response{ - Text: "thing does not exist", - Code: iris.StatusNotFound, - } - - return Try(func() Result { - // if panic because of index exceed the slice - // then the "failure" response will be returned instead. - return Response{Text: things[index]} - }, failure) -} - -func (c *testControllerMethodResult) GetThingWithTryDefaultBy(index int) Result { - return Try(func() Result { - // if panic because of index exceed the slice - // then the default failure response will be returned instead (400 bad request). - return Response{Text: things[index]} - }) -} - -func TestControllerMethodResult(t *testing.T) { - app := iris.New() - New(app).Handle(new(testControllerMethodResult)) - - e := httptest.New(t, app) - - e.GET("/").Expect().Status(iris.StatusOK). - Body().Equal("Hello World!") - - e.GET("/with/status").Expect().Status(iris.StatusNotFound). - Body().Equal("This page doesn't exist") - - e.GET("/json").Expect().Status(iris.StatusOK). - JSON().Equal(iris.Map{ - "name": "Iris", - "age": 2, - }) - - e.GET("/json").WithQuery("err", true).Expect(). - Status(iris.StatusBadRequest). - Body().Equal("error here") - - e.GET("/thing/with/try/1").Expect(). - Status(iris.StatusOK). - Body().Equal("thing 1") - // failure because of index exceed the slice - e.GET("/thing/with/try/3").Expect(). - Status(iris.StatusNotFound). - Body().Equal("thing does not exist") - - e.GET("/thing/with/try/default/3").Expect(). - Status(iris.StatusBadRequest). - Body().Equal("Bad Request") -} - -type testControllerMethodResultTypes struct { - Ctx *context.Context -} - -func (c *testControllerMethodResultTypes) GetText() string { - return "text" -} - -func (c *testControllerMethodResultTypes) GetStatus() int { - return iris.StatusBadGateway -} - -func (c *testControllerMethodResultTypes) GetTextWithStatusOk() (string, int) { - return "OK", iris.StatusOK -} - -// tests should have output arguments mixed -func (c *testControllerMethodResultTypes) GetStatusWithTextNotOkBy(first string, second string) (int, string) { - return iris.StatusForbidden, "NOT_OK_" + first + second -} - -func (c *testControllerMethodResultTypes) GetTextAndContentType() (string, string) { - return "text", "text/html" -} - -type testControllerMethodCustomResult struct { - HTML string -} - -// The only one required function to make that a custom Response dispatcher. -func (r testControllerMethodCustomResult) Dispatch(ctx *context.Context) { - ctx.HTML(r.HTML) -} - -func (c *testControllerMethodResultTypes) GetCustomResponse() testControllerMethodCustomResult { - return testControllerMethodCustomResult{"text"} -} - -func (c *testControllerMethodResultTypes) GetCustomResponseWithStatusOk() (testControllerMethodCustomResult, int) { - return testControllerMethodCustomResult{"OK"}, iris.StatusOK -} - -func (c *testControllerMethodResultTypes) GetCustomResponseWithStatusNotOk() (testControllerMethodCustomResult, int) { - return testControllerMethodCustomResult{"internal server error"}, iris.StatusInternalServerError -} - -func (c *testControllerMethodResultTypes) GetCustomStruct() testCustomStruct { - return testCustomStruct{"Iris", 2} -} - -func (c *testControllerMethodResultTypes) GetCustomStructWithStatusNotOk() (testCustomStruct, int) { - return testCustomStruct{"Iris", 2}, iris.StatusInternalServerError -} - -func (c *testControllerMethodResultTypes) GetCustomStructWithContentType() (testCustomStruct, string) { - return testCustomStruct{"Iris", 2}, "text/xml" -} - -func (c *testControllerMethodResultTypes) GetCustomStructWithError() (s testCustomStruct, err error) { - s = testCustomStruct{"Iris", 2} - if c.Ctx.URLParamExists("err") { - err = errors.New("omit return of testCustomStruct and fire error") - } - - // it should send the testCustomStruct as JSON if error is nil - // otherwise it should fire the default error(BadRequest) with the error's text. - return -} - -func TestControllerMethodResultTypes(t *testing.T) { - app := iris.New() - New(app).Handle(new(testControllerMethodResultTypes)) - - e := httptest.New(t, app) - - e.GET("/text").Expect().Status(iris.StatusOK). - Body().Equal("text") - - e.GET("/status").Expect().Status(iris.StatusBadGateway) - - e.GET("/text/with/status/ok").Expect().Status(iris.StatusOK). - Body().Equal("OK") - - e.GET("/status/with/text/not/ok/first/second").Expect().Status(iris.StatusForbidden). - Body().Equal("NOT_OK_firstsecond") - // Author's note: <-- if that fails means that the last binder called for both input args, - // see path_param_binder.go - - e.GET("/text/and/content/type").Expect().Status(iris.StatusOK). - ContentType("text/html", "utf-8"). - Body().Equal("text") - - e.GET("/custom/response").Expect().Status(iris.StatusOK). - ContentType("text/html", "utf-8"). - Body().Equal("text") - e.GET("/custom/response/with/status/ok").Expect().Status(iris.StatusOK). - ContentType("text/html", "utf-8"). - Body().Equal("OK") - e.GET("/custom/response/with/status/not/ok").Expect().Status(iris.StatusInternalServerError). - ContentType("text/html", "utf-8"). - Body().Equal("internal server error") - - expectedResultFromCustomStruct := map[string]interface{}{ - "name": "Iris", - "age": 2, - } - e.GET("/custom/struct").Expect().Status(iris.StatusOK). - JSON().Equal(expectedResultFromCustomStruct) - e.GET("/custom/struct/with/status/not/ok").Expect().Status(iris.StatusInternalServerError). - JSON().Equal(expectedResultFromCustomStruct) - e.GET("/custom/struct/with/content/type").Expect().Status(iris.StatusOK). - ContentType("text/xml", "utf-8") - e.GET("/custom/struct/with/error").Expect().Status(iris.StatusOK). - JSON().Equal(expectedResultFromCustomStruct) - e.GET("/custom/struct/with/error").WithQuery("err", true).Expect(). - Status(iris.StatusBadRequest). // the default status code if error is not nil - // the content should be not JSON it should be the status code's text - // it will fire the error's text - Body().Equal("omit return of testCustomStruct and fire error") -} - -type testControllerViewResultRespectCtxViewData struct { - T *testing.T -} - -func (t *testControllerViewResultRespectCtxViewData) BeginRequest(ctx *context.Context) { - ctx.ViewData("name_begin", "iris_begin") -} - -func (t *testControllerViewResultRespectCtxViewData) EndRequest(ctx *context.Context) { - // check if data is not overridden by return View {Data: context.Map...} - - dataWritten := ctx.GetViewData() - if dataWritten == nil { - t.T.Fatalf("view data is nil, both BeginRequest and Get failed to write the data") - return - } - - if dataWritten["name_begin"] == nil { - t.T.Fatalf(`view data[name_begin] is nil, - BeginRequest's ctx.ViewData call have been overridden by Get's return View {Data: }. - Total view data: %v`, dataWritten) - } - - if dataWritten["name"] == nil { - t.T.Fatalf("view data[name] is nil, Get's return View {Data: } didn't work. Total view data: %v", dataWritten) - } -} - -func (t *testControllerViewResultRespectCtxViewData) Get() Result { - return View{ - Name: "doesnt_exists.html", - Data: context.Map{"name": "iris"}, // we care about this only. - Code: iris.StatusInternalServerError, - } -} - -func TestControllerViewResultRespectCtxViewData(t *testing.T) { - app := iris.New() - m := New(app.Party("/")) - m.Register(t) - m.Handle(new(testControllerViewResultRespectCtxViewData)) - - e := httptest.New(t, app) - - e.GET("/").Expect().Status(iris.StatusInternalServerError) -} diff --git a/mvc/controller_test.go b/mvc/controller_test.go deleted file mode 100644 index 62233b35c5..0000000000 --- a/mvc/controller_test.go +++ /dev/null @@ -1,758 +0,0 @@ -// black-box testing -package mvc_test - -import ( - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/router" - "github.com/kataras/iris/v12/hero" - "github.com/kataras/iris/v12/httptest" - - . "github.com/kataras/iris/v12/mvc" -) - -type testController struct { - Ctx *context.Context -} - -var writeMethod = func(ctx *context.Context) { - ctx.Writef(ctx.Method()) -} - -func (c *testController) Get() { - writeMethod(c.Ctx) -} - -func (c *testController) Post() { - writeMethod(c.Ctx) -} - -func (c *testController) Put() { - writeMethod(c.Ctx) -} - -func (c *testController) Delete() { - writeMethod(c.Ctx) -} - -func (c *testController) Connect() { - writeMethod(c.Ctx) -} - -func (c *testController) Head() { - writeMethod(c.Ctx) -} - -func (c *testController) Patch() { - writeMethod(c.Ctx) -} - -func (c *testController) Options() { - writeMethod(c.Ctx) -} - -func (c *testController) Trace() { - writeMethod(c.Ctx) -} - -type ( - testControllerAll struct{ Ctx *context.Context } - testControllerAny struct{ Ctx *context.Context } // exactly the same as All. -) - -func (c *testControllerAll) All() { - writeMethod(c.Ctx) -} - -func (c *testControllerAny) Any() { - writeMethod(c.Ctx) -} - -func TestControllerMethodFuncs(t *testing.T) { - app := iris.New() - - New(app).Handle(new(testController)) - New(app.Party("/all")).Handle(new(testControllerAll)) - New(app.Party("/any")).Handle(new(testControllerAny)) - - e := httptest.New(t, app) - for _, method := range router.AllMethods { - - e.Request(method, "/").Expect().Status(iris.StatusOK). - Body().Equal(method) - - e.Request(method, "/all").Expect().Status(iris.StatusOK). - Body().Equal(method) - - e.Request(method, "/any").Expect().Status(iris.StatusOK). - Body().Equal(method) - } -} - -type testControllerBeginAndEndRequestFunc struct { - Ctx *context.Context - - Username string -} - -// called before of every method (Get() or Post()). -// -// useful when more than one methods using the -// same request values or context's function calls. -func (c *testControllerBeginAndEndRequestFunc) BeginRequest(ctx *context.Context) { - c.Username = ctx.Params().Get("username") -} - -// called after every method (Get() or Post()). -func (c *testControllerBeginAndEndRequestFunc) EndRequest(ctx *context.Context) { - ctx.Writef("done") // append "done" to the response -} - -func (c *testControllerBeginAndEndRequestFunc) Get() { - c.Ctx.Writef(c.Username) -} - -func (c *testControllerBeginAndEndRequestFunc) Post() { - c.Ctx.Writef(c.Username) -} - -func TestControllerBeginAndEndRequestFunc(t *testing.T) { - app := iris.New() - New(app.Party("/profile/{username}")). - Handle(new(testControllerBeginAndEndRequestFunc)) - - e := httptest.New(t, app) - usernames := []string{ - "kataras", - "makis", - "efi", - "rg", - "bill", - "whoisyourdaddy", - } - doneResponse := "done" - - for _, username := range usernames { - e.GET("/profile/" + username).Expect().Status(iris.StatusOK). - Body().Equal(username + doneResponse) - e.POST("/profile/" + username).Expect().Status(iris.StatusOK). - Body().Equal(username + doneResponse) - } -} - -func TestControllerBeginAndEndRequestFuncBindMiddleware(t *testing.T) { - app := iris.New() - usernames := map[string]bool{ - "kataras": true, - "makis": false, - "efi": true, - "rg": false, - "bill": true, - "whoisyourdaddy": false, - } - middlewareCheck := func(ctx *context.Context) { - for username, allow := range usernames { - if ctx.Params().Get("username") == username && allow { - ctx.Next() - return - } - } - - ctx.StatusCode(iris.StatusForbidden) - ctx.Writef("forbidden") - } - - app.PartyFunc("/profile/{username}", func(r iris.Party) { - r.Use(middlewareCheck) - New(r).Handle(new(testControllerBeginAndEndRequestFunc)) - }) - - e := httptest.New(t, app) - - doneResponse := "done" - - for username, allow := range usernames { - getEx := e.GET("/profile/" + username).Expect() - if allow { - getEx.Status(iris.StatusOK). - Body().Equal(username + doneResponse) - } else { - getEx.Status(iris.StatusForbidden).Body().Equal("forbidden") - } - - postEx := e.POST("/profile/" + username).Expect() - if allow { - postEx.Status(iris.StatusOK). - Body().Equal(username + doneResponse) - } else { - postEx.Status(iris.StatusForbidden).Body().Equal("forbidden") - } - } -} - -type Model struct { - Username string -} - -type testControllerEndRequestAwareness struct { - Ctx *context.Context -} - -func (c *testControllerEndRequestAwareness) Get() { - username := c.Ctx.Params().Get("username") - c.Ctx.Values().Set(c.Ctx.Application().ConfigurationReadOnly().GetViewDataContextKey(), - map[string]interface{}{ - "TestModel": Model{Username: username}, - "myModel": Model{Username: username + "2"}, - }) -} - -func writeModels(ctx *context.Context, names ...string) { - if expected, got := len(names), len(ctx.GetViewData()); expected != got { - ctx.Writef("expected view data length: %d but got: %d for names: %s", expected, got, names) - return - } - - for _, name := range names { - - m, ok := ctx.GetViewData()[name] - if !ok { - ctx.Writef("fail load and set the %s", name) - return - } - - model, ok := m.(Model) - if !ok { - ctx.Writef("fail to override the %s' name by the tag", name) - return - } - - ctx.Writef(model.Username) - } -} - -func (c *testControllerEndRequestAwareness) BeginRequest(ctx *context.Context) {} -func (c *testControllerEndRequestAwareness) EndRequest(ctx *context.Context) { - writeModels(ctx, "TestModel", "myModel") -} - -func TestControllerEndRequestAwareness(t *testing.T) { - app := iris.New() - New(app.Party("/era/{username}")).Handle(new(testControllerEndRequestAwareness)) - - e := httptest.New(t, app) - usernames := []string{ - "kataras", - "makis", - } - - for _, username := range usernames { - e.GET("/era/" + username).Expect().Status(iris.StatusOK). - Body().Equal(username + username + "2") - } -} - -type testBindType struct { - title string -} - -type testControllerBindStruct struct { - Ctx *context.Context - - // should start with upper letter of course - TitlePointer *testBindType // should have the value of the "myTitlePtr" on test - TitleValue testBindType // should have the value of the "myTitleV" on test - Other string // just another type to check the field collection, should be empty -} - -func (t *testControllerBindStruct) Get() { - t.Ctx.Writef(t.TitlePointer.title + t.TitleValue.title + t.Other) -} - -// test if context can be binded to the controller's function -// without need to declare it to a struct if not needed. -func (t *testControllerBindStruct) GetCtx(ctx iris.Context) { - ctx.StatusCode(iris.StatusContinue) -} - -type testControllerBindDeep struct { - testControllerBindStruct -} - -func (t *testControllerBindDeep) BeforeActivation(b BeforeActivation) { - b.Dependencies().Register(func(ctx iris.Context) (v testCustomStruct, err error) { - err = ctx.ReadJSON(&v) - return - }) -} - -func (t *testControllerBindDeep) Get() { - // t.testControllerBindStruct.Get() - t.Ctx.Writef(t.TitlePointer.title + t.TitleValue.title + t.Other) -} - -func (t *testControllerBindDeep) Post(v testCustomStruct) string { - return v.Name -} - -func TestControllerDependencies(t *testing.T) { - app := iris.New() - // app.Logger().SetLevel("debug") - - t1, t2 := "my pointer title", "val title" - // test bind pointer to pointer of the correct type - myTitlePtr := &testBindType{title: t1} - // test bind value to value of the correct type - myTitleV := testBindType{title: t2} - m := New(app) - m.Register(myTitlePtr, myTitleV) - m.Handle(new(testControllerBindStruct)) - m.Clone(app.Party("/deep")).Handle(new(testControllerBindDeep)) - - e := httptest.New(t, app) - expected := t1 + t2 - e.GET("/").Expect().Status(iris.StatusOK). - Body().Equal(expected) - e.GET("/ctx").Expect().Status(iris.StatusContinue) - - e.GET("/deep").Expect().Status(iris.StatusOK). - Body().Equal(expected) - - e.POST("/deep").WithJSON(iris.Map{"name": "kataras"}).Expect().Status(iris.StatusOK). - Body().Equal("kataras") - - e.POST("/deep").Expect().Status(iris.StatusBadRequest). - Body().Equal("unexpected end of JSON input") -} - -type testCtrl0 struct { - testCtrl00 -} - -func (c *testCtrl0) Get() string { - return c.Ctx.Params().Get("username") -} - -func (c *testCtrl0) EndRequest(ctx *context.Context) { - if c.TitlePointer == nil { - ctx.Writef("\nTitlePointer is nil!\n") - } else { - ctx.Writef(c.TitlePointer.title) - } - - // should be the same as `.testCtrl000.testCtrl0000.EndRequest(ctx)` - c.testCtrl00.EndRequest(ctx) -} - -type testCtrl00 struct { - Ctx *context.Context - - testCtrl000 -} - -type testCtrl000 struct { - testCtrl0000 - - TitlePointer *testBindType -} - -type testCtrl0000 struct { -} - -func (c *testCtrl0000) BeginRequest(ctx *context.Context) {} -func (c *testCtrl0000) EndRequest(ctx *context.Context) { - ctx.Writef("finish") -} - -func TestControllerInsideControllerRecursively(t *testing.T) { - var ( - username = "gerasimos" - title = "mytitle" - expected = username + title + "finish" - ) - - app := iris.New() - m := New(app.Party("/user/{username}")) - m.Register(&testBindType{title: title}) - m.Handle(new(testCtrl0)) - - e := httptest.New(t, app) - e.GET("/user/" + username).Expect(). - Status(iris.StatusOK).Body().Equal(expected) -} - -type testControllerRelPathFromFunc struct{} - -func (c *testControllerRelPathFromFunc) BeginRequest(ctx *context.Context) {} -func (c *testControllerRelPathFromFunc) EndRequest(ctx *context.Context) { - ctx.Writef("%s:%s", ctx.Method(), ctx.Path()) -} - -func (c *testControllerRelPathFromFunc) Get() {} -func (c *testControllerRelPathFromFunc) GetBy(uint64) {} -func (c *testControllerRelPathFromFunc) GetUint8RatioBy(uint8) {} -func (c *testControllerRelPathFromFunc) GetInt64RatioBy(int64) {} -func (c *testControllerRelPathFromFunc) GetAnythingByWildcard(string) {} - -func (c *testControllerRelPathFromFunc) GetLogin() {} -func (c *testControllerRelPathFromFunc) PostLogin() {} - -func (c *testControllerRelPathFromFunc) GetAdminLogin() {} - -func (c *testControllerRelPathFromFunc) PutSomethingIntoThis() {} - -func (c *testControllerRelPathFromFunc) GetSomethingBy(bool) {} - -func (c *testControllerRelPathFromFunc) GetSomethingByBy(string, int) {} - -func (c *testControllerRelPathFromFunc) GetSomethingNewBy(string, int) {} // two input arguments, one By which is the latest word. -func (c *testControllerRelPathFromFunc) GetSomethingByElseThisBy(bool, int) {} // two input arguments - -func (c *testControllerRelPathFromFunc) GetLocationX() {} -func (c *testControllerRelPathFromFunc) GetLocationXY() {} -func (c *testControllerRelPathFromFunc) GetLocationZBy(int) {} - -func TestControllerRelPathFromFunc(t *testing.T) { - app := iris.New() - New(app).Handle(new(testControllerRelPathFromFunc)) - - e := httptest.New(t, app) - e.GET("/").Expect().Status(iris.StatusOK). - Body().Equal("GET:/") - - e.GET("/18446744073709551615").Expect().Status(iris.StatusOK). - Body().Equal("GET:/18446744073709551615") - e.GET("/uint8/ratio/255").Expect().Status(iris.StatusOK). - Body().Equal("GET:/uint8/ratio/255") - e.GET("/uint8/ratio/256").Expect().Status(iris.StatusNotFound) - e.GET("/int64/ratio/-42").Expect().Status(iris.StatusOK). - Body().Equal("GET:/int64/ratio/-42") - e.GET("/something/true").Expect().Status(iris.StatusOK). - Body().Equal("GET:/something/true") - e.GET("/something/false").Expect().Status(iris.StatusOK). - Body().Equal("GET:/something/false") - e.GET("/something/truee").Expect().Status(iris.StatusNotFound) - e.GET("/something/falsee").Expect().Status(iris.StatusNotFound) - e.GET("/something/kataras/42").Expect().Status(iris.StatusOK). - Body().Equal("GET:/something/kataras/42") - e.GET("/something/new/kataras/42").Expect().Status(iris.StatusOK). - Body().Equal("GET:/something/new/kataras/42") - e.GET("/something/true/else/this/42").Expect().Status(iris.StatusOK). - Body().Equal("GET:/something/true/else/this/42") - - e.GET("/login").Expect().Status(iris.StatusOK). - Body().Equal("GET:/login") - e.POST("/login").Expect().Status(iris.StatusOK). - Body().Equal("POST:/login") - e.GET("/admin/login").Expect().Status(iris.StatusOK). - Body().Equal("GET:/admin/login") - e.PUT("/something/into/this").Expect().Status(iris.StatusOK). - Body().Equal("PUT:/something/into/this") - e.GET("/42").Expect().Status(iris.StatusOK). - Body().Equal("GET:/42") - e.GET("/anything/here").Expect().Status(iris.StatusOK). - Body().Equal("GET:/anything/here") - - e.GET("/location/x").Expect().Status(iris.StatusOK). - Body().Equal("GET:/location/x") - e.GET("/location/x/y").Expect().Status(iris.StatusOK). - Body().Equal("GET:/location/x/y") - e.GET("/location/z/42").Expect().Status(iris.StatusOK). - Body().Equal("GET:/location/z/42") -} - -type testControllerActivateListener struct { - TitlePointer *testBindType -} - -func (c *testControllerActivateListener) BeforeActivation(b BeforeActivation) { - b.Dependencies().Register(&testBindType{title: "overrides the dependency but not the field"}) // overrides the `Register` previous calls. - - // b.Handle("POST", "/me/tos-read", "MeTOSRead") - // b.Handle("GET", "/me/tos-read", "MeTOSRead") - // OR: - b.HandleMany("GET POST", "/me/tos-read", "MeTOSRead") -} - -func (c *testControllerActivateListener) Get() string { - return c.TitlePointer.title -} - -func (c *testControllerActivateListener) MeTOSRead() string { - return "MeTOSRead" -} - -func TestControllerActivateListener(t *testing.T) { - app := iris.New() - New(app).Handle(new(testControllerActivateListener)) - m := New(app) - m.Register(&testBindType{ - title: "my title", - }) - m.Party("/manual").Handle(new(testControllerActivateListener)) - // or - m.Party("/manual2").Handle(&testControllerActivateListener{ - TitlePointer: &testBindType{ - title: "my manual title", - }, - }) - - e := httptest.New(t, app) - e.GET("/").Expect().Status(iris.StatusOK). - Body().Equal("overrides the dependency but not the field") - e.GET("/me/tos-read").Expect().Status(iris.StatusOK). - Body().Equal("MeTOSRead") - e.POST("/me/tos-read").Expect().Status(iris.StatusOK). - Body().Equal("MeTOSRead") - - e.GET("/manual").Expect().Status(iris.StatusOK). - Body().Equal("overrides the dependency but not the field") - e.GET("/manual2").Expect().Status(iris.StatusOK). - Body().Equal("my manual title") -} - -type testControllerNotCreateNewDueManuallySettingAllFields struct { - T *testing.T - - TitlePointer *testBindType -} - -func (c *testControllerNotCreateNewDueManuallySettingAllFields) AfterActivation(a AfterActivation) { - if n := len(a.DependenciesReadOnly()) - len(hero.BuiltinDependencies) - 1; /* Application */ n != 1 { - c.T.Fatalf(`expecting 1 dependency; -- the 'T' and the 'TitlePointer' are manually binded (nonzero fields on initilization) -- controller has no more than these two fields, it's a singleton -- however, the dependencies length here should be 1 because the injector's options handler dependencies contains the controller's value dependency itself --- got dependencies length: %d`, n) - } - - if !a.Singleton() { - c.T.Fatalf(`this controller should be tagged as Singleton. It shouldn't be tagged used as request scoped(create new instances on each request), - it doesn't contain any dynamic value or dependencies that should be binded via the iris mvc engine`) - } -} - -func (c *testControllerNotCreateNewDueManuallySettingAllFields) Get() string { - return c.TitlePointer.title -} - -func TestControllerNotCreateNewDueManuallySettingAllFields(t *testing.T) { - app := iris.New() - New(app).Handle(&testControllerNotCreateNewDueManuallySettingAllFields{ - T: t, - TitlePointer: &testBindType{ - title: "my title", - }, - }) - - e := httptest.New(t, app) - e.GET("/").Expect().Status(iris.StatusOK). - Body().Equal("my title") -} - -type testControllerRequestScopedDependencies struct { - MyContext *testMyContext - CustomStruct *testCustomStruct -} - -func (c *testControllerRequestScopedDependencies) Get() *testCustomStruct { - return c.CustomStruct -} - -func (c *testControllerRequestScopedDependencies) GetCustomContext() string { - return c.MyContext.OtherField -} - -func newRequestDep1(ctx *context.Context) *testCustomStruct { - return &testCustomStruct{ - Name: ctx.URLParam("name"), - Age: ctx.URLParamIntDefault("age", 0), - } -} - -type testMyContext struct { - Context *context.Context - OtherField string -} - -func newRequestDep2(ctx *context.Context) *testMyContext { - return &testMyContext{ - Context: ctx, - OtherField: "test", - } -} - -func TestControllerRequestScopedDependencies(t *testing.T) { - app := iris.New() - m := New(app) - m.Register(newRequestDep1) - m.Register(newRequestDep2) - m.Handle(new(testControllerRequestScopedDependencies)) - - e := httptest.New(t, app) - e.GET("/").WithQuery("name", "kataras").WithQuery("age", 27). - Expect().Status(httptest.StatusOK).JSON().Equal(&testCustomStruct{ - Name: "kataras", - Age: 27, - }) - e.GET("/custom/context").Expect().Status(httptest.StatusOK).Body().Equal("test") -} - -type ( - testServiceDoSomething struct{} - - TestControllerAsDeepDep struct { - Ctx iris.Context - Service *testServiceDoSomething - } - - FooController struct { - TestControllerAsDeepDep - } - - BarController struct { - FooController - } - - FinalController struct { - BarController - } -) - -func (s *testServiceDoSomething) DoSomething(ctx iris.Context) { - ctx.WriteString("foo bar") -} - -func (c *FinalController) GetSomething() { - c.Service.DoSomething(c.Ctx) -} - -func TestControllersInsideControllerDeep(t *testing.T) { - app := iris.New() - m := New(app) - m.Register(new(testServiceDoSomething)) - m.Handle(new(FinalController)) - - e := httptest.New(t, app) - e.GET("/something").Expect().Status(httptest.StatusOK).Body().Equal("foo bar") -} - -type testApplicationDependency struct { - App *Application -} - -func (c *testApplicationDependency) Get() string { - return c.App.Name -} - -func TestApplicationDependency(t *testing.T) { - app := iris.New() - m := New(app).SetName("app1") - m.Handle(new(testApplicationDependency)) - - m2 := m.Clone(app.Party("/other")).SetName("app2") - m2.Handle(new(testApplicationDependency)) - - e := httptest.New(t, app) - e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal("app1") - e.GET("/other").Expect().Status(httptest.StatusOK).Body().Equal("app2") -} - -// Authenticated type. -type Authenticated int64 - -// BasePrivateController base controller for private controllers. -type BasePrivateController struct { - CurrentUserID Authenticated - Ctx iris.Context // not-used. -} - -type publicController struct { - Ctx iris.Context // not-used. -} - -func (c *publicController) Get() iris.Map { - return iris.Map{"data": "things"} -} - -type privateController struct{ BasePrivateController } - -func (c *privateController) Get() iris.Map { - return iris.Map{"id": c.CurrentUserID} -} - -func TestControllerOverlapping(t *testing.T) { - app := iris.New() - - m := New(app) - m.Router.SetRegisterRule(iris.RouteOverlap) - - m.Register(func(ctx iris.Context) Authenticated { - if ctx.URLParam("name") == "kataras" { - return 1 - } - - ctx.StopWithStatus(iris.StatusForbidden) - return -1 - }) - - // Order matters. - m.Handle(new(privateController)) - m.Handle(new(publicController)) - - e := httptest.New(t, app) - e.GET("/").WithQuery("name", "kataras").Expect().Status(httptest.StatusOK). - JSON().Equal(iris.Map{"id": 1}) - e.GET("/").Expect().Status(httptest.StatusOK). - JSON().Equal(iris.Map{"data": "things"}) -} - -type testControllerMethodHandlerBindStruct struct{} - -type bindStructData struct { - Name string `json:"name" url:"name"` -} - -func (*testControllerMethodHandlerBindStruct) Any(data bindStructData) bindStructData { - return data -} - -func (*testControllerMethodHandlerBindStruct) PostBySlice(id uint64, manyData []bindStructData) []bindStructData { - return manyData -} - -type dataSlice []bindStructData - -func (*testControllerMethodHandlerBindStruct) PostBySlicetype(id uint64, manyData dataSlice) dataSlice { - return manyData -} - -type dataSlicePtr []*bindStructData - -func (*testControllerMethodHandlerBindStruct) PostBySlicetypeptr(id uint64, manyData dataSlicePtr) dataSlicePtr { - return manyData -} - -func TestControllerMethodHandlerBindStruct(t *testing.T) { - app := iris.New() - - m := New(app.Party("/data")) - m.HandleError(func(ctx iris.Context, err error) { - t.Fatalf("Path: %s, Error: %v", ctx.Path(), err) - }) - - m.Handle(new(testControllerMethodHandlerBindStruct)) - - data := bindStructData{Name: "kataras"} - manyData := []bindStructData{data, {"john doe"}} - - e := httptest.New(t, app) - e.GET("/data").WithQueryObject(data).Expect().Status(httptest.StatusOK).JSON().Equal(data) - e.PATCH("/data").WithJSON(data).Expect().Status(httptest.StatusOK).JSON().Equal(data) - e.POST("/data/42/slice").WithJSON(manyData).Expect().Status(httptest.StatusOK).JSON().Equal(manyData) - e.POST("/data/42/slicetype").WithJSON(manyData).Expect().Status(httptest.StatusOK).JSON().Equal(manyData) - e.POST("/data/42/slicetypeptr").WithJSON(manyData).Expect().Status(httptest.StatusOK).JSON().Equal(manyData) - // more tests inside the hero package itself. -} diff --git a/mvc/grpc.go b/mvc/grpc.go index e488a74cb6..554e170fc3 100644 --- a/mvc/grpc.go +++ b/mvc/grpc.go @@ -4,7 +4,7 @@ import ( "net/http" "path" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) // GRPC registers a controller which serves gRPC clients. diff --git a/mvc/mvc.go b/mvc/mvc.go index 7309934de0..cf59f1efa9 100644 --- a/mvc/mvc.go +++ b/mvc/mvc.go @@ -4,10 +4,10 @@ import ( "reflect" "strings" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/router" - "github.com/kataras/iris/v12/hero" - "github.com/kataras/iris/v12/websocket" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/router" + "github.com/kataras/iris/hero" + "github.com/kataras/iris/websocket" "github.com/kataras/golog" ) diff --git a/mvc/versioning.go b/mvc/versioning.go index c16c28892a..2d1cf0f246 100644 --- a/mvc/versioning.go +++ b/mvc/versioning.go @@ -1,9 +1,9 @@ package mvc import ( - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/router" - "github.com/kataras/iris/v12/versioning" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/router" + "github.com/kataras/iris/versioning" ) // Version returns a valid `Option` that can be passed to the `Application.Handle` method. diff --git a/sessions/config.go b/sessions/config.go index e673d50ef1..851092fc77 100644 --- a/sessions/config.go +++ b/sessions/config.go @@ -3,7 +3,7 @@ package sessions import ( "time" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" "github.com/google/uuid" ) diff --git a/sessions/database.go b/sessions/database.go index a32f0fbdfa..c444b828fb 100644 --- a/sessions/database.go +++ b/sessions/database.go @@ -5,7 +5,7 @@ import ( "sync" "time" - "github.com/kataras/iris/v12/core/memstore" + "github.com/kataras/iris/core/memstore" ) // ErrNotImplemented is returned when a particular feature is not yet implemented yet. diff --git a/sessions/lifetime.go b/sessions/lifetime.go index b92c4d8d6e..08ef2290dc 100644 --- a/sessions/lifetime.go +++ b/sessions/lifetime.go @@ -4,7 +4,7 @@ import ( "sync" "time" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) // LifeTime controls the session expiration datetime. diff --git a/sessions/session.go b/sessions/session.go index f4110ec029..100b3e52a9 100644 --- a/sessions/session.go +++ b/sessions/session.go @@ -6,7 +6,7 @@ import ( "strconv" "sync" - "github.com/kataras/iris/v12/core/memstore" + "github.com/kataras/iris/core/memstore" ) type ( diff --git a/sessions/sessiondb/badger/database.go b/sessions/sessiondb/badger/database.go index c7accb7cd3..863c9712f6 100644 --- a/sessions/sessiondb/badger/database.go +++ b/sessions/sessiondb/badger/database.go @@ -8,7 +8,7 @@ import ( "sync/atomic" "time" - "github.com/kataras/iris/v12/sessions" + "github.com/kataras/iris/sessions" "github.com/dgraph-io/badger/v2" "github.com/kataras/golog" diff --git a/sessions/sessiondb/boltdb/database.go b/sessions/sessiondb/boltdb/database.go index 90e884b83f..eb345775d6 100644 --- a/sessions/sessiondb/boltdb/database.go +++ b/sessions/sessiondb/boltdb/database.go @@ -7,7 +7,7 @@ import ( "runtime" "time" - "github.com/kataras/iris/v12/sessions" + "github.com/kataras/iris/sessions" "github.com/kataras/golog" bolt "go.etcd.io/bbolt" diff --git a/sessions/sessiondb/redis/database.go b/sessions/sessiondb/redis/database.go index b3453831a0..a4c2b8253d 100644 --- a/sessions/sessiondb/redis/database.go +++ b/sessions/sessiondb/redis/database.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "github.com/kataras/iris/v12/sessions" + "github.com/kataras/iris/sessions" "github.com/kataras/golog" ) diff --git a/sessions/sessions.go b/sessions/sessions.go index b95b03a256..3ee8ca01a2 100644 --- a/sessions/sessions.go +++ b/sessions/sessions.go @@ -4,7 +4,7 @@ import ( "net/http" "time" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) func init() { diff --git a/sessions/sessions_test.go b/sessions/sessions_test.go deleted file mode 100644 index f9c4b2cf77..0000000000 --- a/sessions/sessions_test.go +++ /dev/null @@ -1,326 +0,0 @@ -package sessions_test - -import ( - "sync" - "testing" - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/httptest" - "github.com/kataras/iris/v12/sessions" -) - -func TestSessions(t *testing.T) { - app := iris.New() - - sess := sessions.New(sessions.Config{Cookie: "mycustomsessionid"}) - app.Use(sess.Handler()) - - testSessions(t, app) -} - -const ( - testEnableSubdomain = true -) - -func testSessions(t *testing.T, app *iris.Application) { - values := map[string]interface{}{ - "Name": "iris", - "Months": "4", - "Secret": "dsads£2132215£%%Ssdsa", - } - - writeValues := func(ctx *context.Context) { - s := sessions.Get(ctx) - sessValues := s.GetAll() - - _, err := ctx.JSON(sessValues) - if err != nil { - t.Fatal(err) - } - } - - if testEnableSubdomain { - app.Party("subdomain.").Get("/get", writeValues) - } - - app.Post("/set", func(ctx *context.Context) { - s := sessions.Get(ctx) - vals := make(map[string]interface{}) - if err := ctx.ReadJSON(&vals); err != nil { - t.Fatalf("Cannot read JSON. Trace %s", err.Error()) - } - for k, v := range vals { - s.Set(k, v) - } - }) - - app.Get("/get", func(ctx *context.Context) { - writeValues(ctx) - }) - - app.Get("/clear", func(ctx *context.Context) { - sessions.Get(ctx).Clear() - writeValues(ctx) - }) - - app.Get("/destroy", func(ctx *context.Context) { - session := sessions.Get(ctx) - if session.IsNew() { - t.Fatal("expected session not to be nil on destroy") - } - - session.Man.Destroy(ctx) - - if sessions.Get(ctx) != nil { - t.Fatal("expected session inside Context to be nil after Manager's Destroy call") - } - - ctx.JSON(struct{}{}) - // the cookie and all values should be empty - }) - - // cookie should be new. - app.Get("/after_destroy_renew", func(ctx *context.Context) { - isNew := sessions.Get(ctx).IsNew() - ctx.Writef("%v", isNew) - }) - - app.Get("/multi_start_set_get", func(ctx *context.Context) { - s := sessions.Get(ctx) - s.Set("key", "value") - ctx.Next() - }, func(ctx *context.Context) { - s := sessions.Get(ctx) - _, err := ctx.Writef(s.GetString("key")) - if err != nil { - t.Fatal(err) - } - }) - - e := httptest.New(t, app, httptest.URL("http://example.com")) - - e.POST("/set").WithJSON(values).Expect().Status(iris.StatusOK).Cookies().NotEmpty() - e.GET("/get").Expect().Status(iris.StatusOK).JSON().Object().Equal(values) - if testEnableSubdomain { - es := e.Builder(func(req *httptest.Request) { - req.WithURL("http://subdomain.example.com") - }) - es.Request("GET", "/get").Expect().Status(iris.StatusOK).JSON().Object().Equal(values) - } - // test destroy which also clears first - d := e.GET("/destroy").Expect().Status(iris.StatusOK) - d.JSON().Object().Empty() - - d = e.GET("/after_destroy_renew").Expect().Status(iris.StatusOK) - d.Body().Equal("true") - d.Cookies().NotEmpty() - - // set and clear again - e.POST("/set").WithJSON(values).Expect().Status(iris.StatusOK) - e.GET("/clear").Expect().Status(iris.StatusOK).JSON().Object().Empty() - - // test start on the same request but more than one times - - e.GET("/multi_start_set_get").Expect().Status(iris.StatusOK).Body().Equal("value") -} - -func TestFlashMessages(t *testing.T) { - app := iris.New() - - sess := sessions.New(sessions.Config{Cookie: "mycustomsessionid"}) - - valueSingleKey := "Name" - valueSingleValue := "iris-sessions" - - values := map[string]interface{}{ - valueSingleKey: valueSingleValue, - "Days": "1", - "Secret": "dsads£2132215£%%Ssdsa", - } - - writeValues := func(ctx *context.Context, values map[string]interface{}) error { - _, err := ctx.JSON(values) - return err - } - - app.Post("/set", func(ctx *context.Context) { - vals := make(map[string]interface{}) - if err := ctx.ReadJSON(&vals); err != nil { - t.Fatalf("Cannot readjson. Trace %s", err.Error()) - } - s := sess.Start(ctx) - for k, v := range vals { - s.SetFlash(k, v) - } - - ctx.StatusCode(iris.StatusOK) - }) - - writeFlashValues := func(ctx *context.Context) { - s := sess.Start(ctx) - - flashes := s.GetFlashes() - if err := writeValues(ctx, flashes); err != nil { - t.Fatalf("While serialize the flash values: %s", err.Error()) - } - } - - app.Get("/get_single", func(ctx *context.Context) { - s := sess.Start(ctx) - flashMsgString := s.GetFlashString(valueSingleKey) - ctx.WriteString(flashMsgString) - }) - - app.Get("/get", func(ctx *context.Context) { - writeFlashValues(ctx) - }) - - app.Get("/clear", func(ctx *context.Context) { - s := sess.Start(ctx) - s.ClearFlashes() - writeFlashValues(ctx) - }) - - app.Get("/destroy", func(ctx *context.Context) { - sess.Destroy(ctx) - writeFlashValues(ctx) - ctx.StatusCode(iris.StatusOK) - // the cookie and all values should be empty - }) - - // request cookie should be empty - app.Get("/after_destroy", func(ctx *context.Context) { - ctx.StatusCode(iris.StatusOK) - }) - - e := httptest.New(t, app, httptest.URL("http://example.com")) - - e.POST("/set").WithJSON(values).Expect().Status(iris.StatusOK).Cookies().NotEmpty() - // get all - e.GET("/get").Expect().Status(iris.StatusOK).JSON().Object().Equal(values) - // get the same flash on other request should return nothing because the flash message is removed after fetch once - e.GET("/get").Expect().Status(iris.StatusOK).JSON().Object().Empty() - // test destroy which also clears first - d := e.GET("/destroy").Expect().Status(iris.StatusOK) - d.JSON().Object().Empty() - e.GET("/after_destroy").Expect().Status(iris.StatusOK).Cookies().Empty() - // set and clear again - e.POST("/set").WithJSON(values).Expect().Status(iris.StatusOK).Cookies().NotEmpty() - e.GET("/clear").Expect().Status(iris.StatusOK).JSON().Object().Empty() - - // set again in order to take the single one ( we don't test Cookies.NotEmpty because httpexpect default conf reads that from the request-only) - e.POST("/set").WithJSON(values).Expect().Status(iris.StatusOK) - e.GET("/get_single").Expect().Status(iris.StatusOK).Body().Equal(valueSingleValue) -} - -func TestSessionsUpdateExpiration(t *testing.T) { - app := iris.New() - - cookieName := "mycustomsessionid" - - sess := sessions.New(sessions.Config{ - Cookie: cookieName, - Expires: 30 * time.Minute, - AllowReclaim: true, - }) - - app.Use(sess.Handler()) - - type response struct { - SessionID string `json:"sessionID"` - Logged bool `json:"logged"` - } - - var writeResponse = func(ctx *context.Context) { - session := sessions.Get(ctx) - ctx.JSON(response{ - SessionID: session.ID(), - Logged: session.GetBooleanDefault("logged", false), - }) - } - - app.Get("/get", func(ctx *context.Context) { - writeResponse(ctx) - }) - - app.Get("/set", func(ctx iris.Context) { - sessions.Get(ctx).Set("logged", true) - writeResponse(ctx) - }) - - app.Post("/remember_me", func(ctx iris.Context) { - // re-sends the cookie with the new Expires and MaxAge fields, - // test checks that on same session id too. - sessions.Get(ctx).Man.UpdateExpiration(ctx, 24*time.Hour) - writeResponse(ctx) - }) - - app.Get("/destroy", func(ctx iris.Context) { - sessions.Get(ctx).Man.Destroy(ctx) // this will delete the cookie too. - }) - - e := httptest.New(t, app, httptest.URL("http://example.com")) - - tt := e.GET("/set").Expect().Status(httptest.StatusOK) - tt.Cookie(cookieName).MaxAge().InRange(29*time.Minute, 30*time.Minute) - sessionID := tt.JSON().Object().Raw()["sessionID"].(string) - - expectedResponse := response{SessionID: sessionID, Logged: true} - e.GET("/get").Expect().Status(httptest.StatusOK). - JSON().Equal(expectedResponse) - - tt = e.POST("/remember_me").Expect().Status(httptest.StatusOK) - tt.Cookie(cookieName).MaxAge().InRange(23*time.Hour, 24*time.Hour) - tt.JSON().Equal(expectedResponse) - - // Test call `UpdateExpiration` when cookie is firstly created. - e.GET("/destroy").Expect().Status(httptest.StatusOK) - e.POST("/remember_me").Expect().Status(httptest.StatusOK). - Cookie(cookieName).MaxAge().InRange(23*time.Hour, 24*time.Hour) -} - -// go test -v -count=100 -run=TestSessionsUpdateExpirationConcurrently$ -// #1488 -func TestSessionsUpdateExpirationConcurrently(t *testing.T) { - cookieName := "mycustomsessionid" - sess := sessions.New(sessions.Config{ - Cookie: cookieName, - Expires: 30 * time.Minute, - AllowReclaim: true, - }) - - app := iris.New() - app.Use(sess.Handler()) - app.Use(func(ctx iris.Context) { - // session will expire after 30 minute at the last visit - sess.UpdateExpiration(ctx, 30*time.Minute) - ctx.Next() - }) - - app.Get("/get", func(ctx iris.Context) { - ctx.WriteString(sessions.Get(ctx).ID()) - }) - - e := httptest.New(t, app, httptest.URL("http://example.com")) - - id := e.GET("/get").Expect().Status(httptest.StatusOK).Body().Raw() - - i := 0 - wg := sync.WaitGroup{} - wg.Add(1000) - for i < 1000 { - go func() { - tt := e.GET("/get").Expect().Status(httptest.StatusOK) - tt.Body().Equal(id) - tt.Cookie(cookieName).MaxAge().InRange(29*time.Minute, 30*time.Minute) - wg.Done() - }() - i++ - } - wg.Wait() - tt := e.GET("/get").Expect() - tt.Status(httptest.StatusOK).Body().Equal(id) - tt.Cookie(cookieName).MaxAge().InRange(29*time.Minute, 30*time.Minute) -} diff --git a/versioning/deprecation.go b/versioning/deprecation.go index 7e28d42111..912740333d 100644 --- a/versioning/deprecation.go +++ b/versioning/deprecation.go @@ -3,7 +3,7 @@ package versioning import ( "time" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) // DeprecationOptions describes the deprecation headers key-values. diff --git a/versioning/deprecation_test.go b/versioning/deprecation_test.go deleted file mode 100644 index 3dfd77b587..0000000000 --- a/versioning/deprecation_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package versioning_test - -import ( - "testing" - "time" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/httptest" - "github.com/kataras/iris/v12/versioning" -) - -func TestDeprecated(t *testing.T) { - app := iris.New() - - writeVesion := func(ctx iris.Context) { - ctx.WriteString(versioning.GetVersion(ctx)) - } - - opts := versioning.DeprecationOptions{ - WarnMessage: "deprecated, see ", - DeprecationDate: time.Now().UTC(), - DeprecationInfo: "a bigger version is available, see for more information", - } - app.Get("/", versioning.Deprecated(writeVesion, opts)) - - e := httptest.New(t, app) - - ex := e.GET("/").WithHeader(versioning.AcceptVersionHeaderKey, "1.0").Expect() - ex.Status(iris.StatusOK).Body().Equal("1.0") - ex.Header("X-API-Warn").Equal(opts.WarnMessage) - expectedDateStr := opts.DeprecationDate.Format(app.ConfigurationReadOnly().GetTimeFormat()) - ex.Header("X-API-Deprecation-Date").Equal(expectedDateStr) -} diff --git a/versioning/group.go b/versioning/group.go index 171d2af900..2df13378b2 100644 --- a/versioning/group.go +++ b/versioning/group.go @@ -1,8 +1,8 @@ package versioning import ( - "github.com/kataras/iris/v12/context" - "github.com/kataras/iris/v12/core/router" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/router" ) // Group is a group of version-based routes. diff --git a/versioning/version.go b/versioning/version.go index b5436d4356..238a9b5adb 100644 --- a/versioning/version.go +++ b/versioning/version.go @@ -4,7 +4,7 @@ import ( "errors" "strings" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) const ( diff --git a/versioning/version_test.go b/versioning/version_test.go deleted file mode 100644 index eab10ea9fa..0000000000 --- a/versioning/version_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package versioning_test - -import ( - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/httptest" - "github.com/kataras/iris/v12/versioning" -) - -func TestGetVersion(t *testing.T) { - app := iris.New() - - writeVesion := func(ctx iris.Context) { - ctx.WriteString(versioning.GetVersion(ctx)) - } - - app.Get("/", writeVesion) - app.Get("/manual", func(ctx iris.Context) { - versioning.SetVersion(ctx, "11.0.5") - ctx.Next() - }, writeVesion) - - e := httptest.New(t, app) - - e.GET("/").WithHeader(versioning.AcceptVersionHeaderKey, "1.0").Expect(). - Status(iris.StatusOK).Body().Equal("1.0") - e.GET("/").WithHeader(versioning.AcceptHeaderKey, "application/vnd.api+json; version=2.1").Expect(). - Status(iris.StatusOK).Body().Equal("2.1") - e.GET("/").WithHeader(versioning.AcceptHeaderKey, "application/vnd.api+json; version=2.1 ;other=dsa").Expect(). - Status(iris.StatusOK).Body().Equal("2.1") - e.GET("/").WithHeader(versioning.AcceptHeaderKey, "version=2.1").Expect(). - Status(iris.StatusOK).Body().Equal("2.1") - e.GET("/").WithHeader(versioning.AcceptHeaderKey, "version=1").Expect(). - Status(iris.StatusOK).Body().Equal("1") - - // unknown versions. - e.GET("/").WithHeader(versioning.AcceptVersionHeaderKey, "").Expect(). - Status(iris.StatusOK).Body().Equal("") - e.GET("/").WithHeader(versioning.AcceptHeaderKey, "application/vnd.api+json; version=").Expect(). - Status(iris.StatusOK).Body().Equal("") - e.GET("/").WithHeader(versioning.AcceptHeaderKey, "application/vnd.api+json; version= ;other=dsa").Expect(). - Status(iris.StatusOK).Body().Equal("") - e.GET("/").WithHeader(versioning.AcceptHeaderKey, "version=").Expect(). - Status(iris.StatusOK).Body().Equal("") - - e.GET("/manual").Expect().Status(iris.StatusOK).Body().Equal("11.0.5") -} diff --git a/versioning/versioning.go b/versioning/versioning.go index fb4d702f1f..9b36b3483c 100644 --- a/versioning/versioning.go +++ b/versioning/versioning.go @@ -1,7 +1,7 @@ package versioning import ( - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" "github.com/hashicorp/go-version" ) diff --git a/versioning/versioning_test.go b/versioning/versioning_test.go deleted file mode 100644 index 536b39bc6c..0000000000 --- a/versioning/versioning_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package versioning_test - -import ( - "testing" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/httptest" - "github.com/kataras/iris/v12/versioning" -) - -func notFoundHandler(ctx iris.Context) { - ctx.NotFound() -} - -const ( - v10Response = "v1.0 handler" - v2Response = "v2.x handler" -) - -func sendHandler(contents string) iris.Handler { - return func(ctx iris.Context) { - ctx.WriteString(contents) - } -} - -func TestIf(t *testing.T) { - if expected, got := true, versioning.If("1.0", ">=1"); expected != got { - t.Fatalf("expected %s to be %s", "1.0", ">= 1") - } - if expected, got := true, versioning.If("1.2.3", "> 1.2"); expected != got { - t.Fatalf("expected %s to be %s", "1.2.3", "> 1.2") - } -} - -func TestNewMatcher(t *testing.T) { - app := iris.New() - - userAPI := app.Party("/api/user") - userAPI.Get("/", versioning.NewMatcher(versioning.Map{ - "1.0": sendHandler(v10Response), - ">= 2, < 3": sendHandler(v2Response), - versioning.NotFound: notFoundHandler, - })) - - // middleware as usual. - myMiddleware := func(ctx iris.Context) { - ctx.Header("X-Custom", "something") - ctx.Next() - } - myVersions := versioning.Map{ - "1.0": sendHandler(v10Response), - } - - userAPI.Get("/with_middleware", myMiddleware, versioning.NewMatcher(myVersions)) - - e := httptest.New(t, app) - - e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "1").Expect(). - Status(iris.StatusOK).Body().Equal(v10Response) - e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.0").Expect(). - Status(iris.StatusOK).Body().Equal(v2Response) - e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.1").Expect(). - Status(iris.StatusOK).Body().Equal(v2Response) - e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.9.9").Expect(). - Status(iris.StatusOK).Body().Equal(v2Response) - - // middleware as usual. - ex := e.GET("/api/user/with_middleware").WithHeader(versioning.AcceptVersionHeaderKey, "1.0").Expect() - ex.Status(iris.StatusOK).Body().Equal(v10Response) - ex.Header("X-Custom").Equal("something") - - e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "3.0").Expect(). - Status(iris.StatusNotFound).Body().Equal("Not Found") -} - -func TestNewGroup(t *testing.T) { - app := iris.New() - - userAPI := app.Party("/api/user") - // [... static serving, middlewares and etc goes here]. - - userAPIV10 := versioning.NewGroup(userAPI, "1.0").Deprecated(versioning.DefaultDeprecationOptions) - // V10middlewareResponse := "m1" - // userAPIV10.Use(func(ctx iris.Context) { - // println("exec userAPIV10.Use - midl1") - // sendHandler(V10middlewareResponse)(ctx) - // ctx.Next() - // }) - // userAPIV10.Use(func(ctx iris.Context) { - // println("exec userAPIV10.Use - midl2") - // sendHandler(V10middlewareResponse + "midl2")(ctx) - // ctx.Next() - // }) - // userAPIV10.Use(func(ctx iris.Context) { - // println("exec userAPIV10.Use - midl3") - // ctx.Next() - // }) - - userAPIV10.Get("/", sendHandler(v10Response)) - userAPIV2 := versioning.NewGroup(userAPI, ">= 2, < 3") - // V2middlewareResponse := "m2" - // userAPIV2.Use(func(ctx iris.Context) { - // println("exec userAPIV2.Use - midl1") - // sendHandler(V2middlewareResponse)(ctx) - // ctx.Next() - // }) - // userAPIV2.Use(func(ctx iris.Context) { - // println("exec userAPIV2.Use - midl2") - // ctx.Next() - // }) - - userAPIV2.Get("/", sendHandler(v2Response)) - userAPIV2.Post("/", sendHandler(v2Response)) - userAPIV2.Put("/other", sendHandler(v2Response)) - - e := httptest.New(t, app) - - ex := e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "1").Expect() - ex.Status(iris.StatusOK).Body().Equal(v10Response) - ex.Header("X-API-Warn").Equal(versioning.DefaultDeprecationOptions.WarnMessage) - - e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.0").Expect(). - Status(iris.StatusOK).Body().Equal(v2Response) - e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.1").Expect(). - Status(iris.StatusOK).Body().Equal(v2Response) - e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2.9.9").Expect(). - Status(iris.StatusOK).Body().Equal(v2Response) - e.POST("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "2").Expect(). - Status(iris.StatusOK).Body().Equal(v2Response) - e.PUT("/api/user/other").WithHeader(versioning.AcceptVersionHeaderKey, "2.9").Expect(). - Status(iris.StatusOK).Body().Equal(v2Response) - - e.GET("/api/user").WithHeader(versioning.AcceptVersionHeaderKey, "3.0").Expect(). - Status(iris.StatusNotImplemented).Body().Equal("version not found") -} diff --git a/view/README.md b/view/README.md index 17599f3d59..e98dec96b0 100644 --- a/view/README.md +++ b/view/README.md @@ -27,7 +27,7 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) files to // file: main.go package main -import "github.com/kataras/iris/v12" +import "github.com/kataras/iris" func main() { app := iris.New() @@ -74,7 +74,7 @@ func main() { ```go package main -import "github.com/kataras/iris/v12" +import "github.com/kataras/iris" func main() { app := iris.New() @@ -124,7 +124,7 @@ Example code: ```go package main -import "github.com/kataras/iris/v12" +import "github.com/kataras/iris" func main() { app := iris.New() diff --git a/view/django.go b/view/django.go index e77f8860b3..09813a14ae 100644 --- a/view/django.go +++ b/view/django.go @@ -11,7 +11,7 @@ import ( "strings" "sync" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" "github.com/iris-contrib/pongo2" ) diff --git a/view/jet.go b/view/jet.go index afab0c7988..a1c5a5d3bc 100644 --- a/view/jet.go +++ b/view/jet.go @@ -9,7 +9,7 @@ import ( "reflect" "strings" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" "github.com/CloudyKit/jet/v4" ) diff --git a/view/view.go b/view/view.go index 05abdf78e1..5da05786c9 100644 --- a/view/view.go +++ b/view/view.go @@ -6,7 +6,7 @@ import ( "path/filepath" "strings" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" ) type ( diff --git a/websocket/websocket.go b/websocket/websocket.go index 5428baffd0..98c45a7025 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -3,7 +3,7 @@ package websocket import ( "net/http" - "github.com/kataras/iris/v12/context" + "github.com/kataras/iris/context" "github.com/kataras/neffos" "github.com/kataras/neffos/gobwas"