From 9f85b74fc909b1f723254634fbd95c19d2e514f5 Mon Sep 17 00:00:00 2001 From: kataras Date: Mon, 10 Jul 2017 18:32:42 +0300 Subject: [PATCH] Add notes for the new lead maintainer of the open-source iris project and align with @get-ion/ion by @hiveminded Former-commit-id: da4f38eb9034daa49446df3ee529423b98f9b331 --- .gitattributes | 3 + .github/CODEOWNERS | 6 + .github/CONTRIBUTING.md | 75 ++- .github/ISSUE_TEMPLATE.md | 7 +- .github/PULL_REQUEST_TEMPLATE.md | 8 +- .gitignore | 4 - .travis.yml | 38 +- ACQUIRED_HISTORY.md | 89 +++ AUTHORS | 5 + CODE-OF-CONDUCT.md | 41 -- Dockerfile | 5 + Dockerfile.build | 12 + Gopkg.lock | 303 --------- Gopkg.toml | 179 ------ HISTORY.md | 153 ++++- LICENSE | 35 +- README.md | 589 +++++------------- _examples/README.md | 256 +++++--- _examples/advanced/url-shortener/shortener.go | 4 - _examples/authentication/README.md | 6 + .../basicauth/main.go | 7 +- .../authentication/basicauth/main_test.go | 29 + .../oauth2/main.go | 54 +- .../oauth2/templates/index.html | 0 .../oauth2/templates/user.html | 0 _examples/beginner/e-mail/main.go | 87 --- .../beginner/e-mail/templates/mail_body.html | 7 - .../static/favicons/iris_favicon_48_48.ico | Bin 15086 -> 0 bytes .../file-server/basic/assets/favicon.ico | Bin 15086 -> 0 bytes .../assets/favicon.ico | Bin 15086 -> 0 bytes _examples/beginner/hello-world/main.go | 14 - .../beginner/listening/listen-unix/main.go | 17 - _examples/beginner/write-json/main.go | 39 -- .../from-configuration-structure}/main.go | 12 +- .../from-toml-file/configs/ion.tml} | 2 +- .../configuration/from-toml-file/main.go | 4 + .../from-yaml-file/configs/ion.yml} | 0 .../configuration/from-yaml-file/main.go | 5 +- .../configuration/functional/main.go | 9 +- .../convert-handlers/negroni-like/main.go | 8 +- .../convert-handlers/nethttp/main.go | 8 +- .../file-server/basic/assets/css/main.css | 0 .../file-server/basic/assets/favicon.ico | Bin 0 -> 1150 bytes .../basic/assets/js/jquery-2.1.1.js | 0 .../{beginner => }/file-server/basic/main.go | 0 .../assets/css/bootstrap.min.css | 0 .../assets/favicon.ico | Bin 0 -> 1150 bytes .../assets/js/jquery-2.1.1.js | 0 .../embedding-files-into-app/bindata.go | 22 +- .../embedding-files-into-app/main.go | 0 .../embedding-files-into-app/main_test.go | 16 +- .../{beginner => file-server}/favicon/main.go | 10 +- .../favicon/static/favicons/favicon.ico | Bin 0 -> 1150 bytes .../send-files/files/first.zip | Bin .../send-files/main.go | 0 .../single-page-application/basic}/main.go | 4 +- .../basic}/main_test.go | 8 +- .../basic}/public/app.js | 0 .../basic}/public/css/main.css | 0 .../basic}/public/index.html | 0 .../bindata.go | 16 +- .../embedded-single-page-application}/main.go | 4 +- .../main_test.go | 11 +- .../public/app.js | 0 .../public/css/main.css | 0 .../public/index.html | 0 _examples/hello-world/main.go | 36 ++ _examples/hello-world/main_go19.go | 35 ++ .../custom-httpserver/ion-way}/main.go | 4 +- .../custom-httpserver/multi/main.go | 0 .../custom-httpserver/std-way/main.go | 6 +- .../custom-listener/main.go | 0 .../custom-listener/unix-reuseport/main.go | 45 ++ .../unix-reuseport/main_windows.go | 7 + .../graceful-shutdown/custom-notifier/main.go | 11 +- .../default-notifier/main.go | 35 ++ .../listen-addr/main.go | 2 +- .../listen-letsencrypt/main.go | 2 +- .../listen-tls/main.go | 0 .../listen-tls/mycert.cert | 0 .../listen-tls/mykey.key | 0 _examples/http-listening/listen-unix/main.go | 19 + _examples/http_request/README.md | 1 + .../read-form/main.go | 2 +- .../read-form/templates/form.html | 0 .../read-json/main.go | 4 +- .../request-logger/main.go | 0 .../upload-files/main.go | 2 +- .../upload-files/templates/upload_form.html | 0 _examples/http_responsewriter/README.md | 1 + .../stream-writer/main.go | 0 .../transactions/main.go | 2 +- .../write-rest}/main.go | 64 +- _examples/intermediate/cloud-editor/main.go | 34 - .../graceful-shutdown/basic/main.go | 48 -- .../graceful-shutdown/custom-host/main.go | 72 --- .../i18n/locales/locale_el-GR.ini | 1 - .../sessions/password-hashing/main.go | 28 - .../multi/public/assets/images/test.ico | Bin 7414 -> 0 bytes .../upload_resources/iris_favicon_32_32.ico | Bin 7414 -> 0 bytes .../intermediate/websockets/overview/main.go | 100 --- .../websockets/overview/static/js/chat.js | 38 -- .../websockets/overview/templates/client.html | 24 - .../file-logger/main.go | 11 +- .../i18n/locales/locale_el-GR.ini | 1 + .../i18n/locales/locale_en-US.ini | 0 .../i18n/locales/locale_zh-CN.ini | 0 .../i18n/main.go | 18 +- _examples/miscellaneous/i18n/main_test.go | 29 + .../{beginner => miscellaneous}/pprof/main.go | 0 .../recover/main.go | 0 _examples/{beginner => }/overview/main.go | 12 +- .../views/users/create_verification.html | 0 .../overview/views/users/profile.html | 0 .../{beginner => }/routing/basic/main.go | 8 +- .../custom-context/method-overriding/main.go | 19 +- .../method-overriding/view/hi.html | 0 .../custom-context/new-implementation/main.go | 104 ++++ .../new-implementation/main_test.go | 26 + .../routing/custom-wrapper/main.go | 4 +- .../routing/custom-wrapper/main_test.go | 3 +- .../routing/custom-wrapper/public/app.js | 0 .../custom-wrapper/public/css/main.css | 0 .../routing/custom-wrapper/public/index.html | 0 .../routing/dynamic-path/main.go | 12 +- .../{beginner => routing}/http-errors/main.go | 0 _examples/{beginner => }/routing/main.go | 87 ++- _examples/routing/main_test.go | 125 ++++ .../{beginner => }/routing/overview/main.go | 12 +- .../{beginner => }/routing/reverse/main.go | 4 +- .../route-state/main.go | 6 +- .../{intermediate => }/subdomains/multi/hosts | 4 +- .../subdomains/multi/main.go | 2 +- .../multi/public/assets/images/test.ico | Bin 0 -> 1150 bytes .../multi/public/upload_resources/favicon.ico | Bin 0 -> 1150 bytes .../subdomains/single/hosts | 4 +- .../subdomains/single/main.go | 0 .../subdomains/wildcard/hosts | 4 +- .../subdomains/wildcard/main.go | 0 .../{intermediate => }/subdomains/www/hosts | 8 +- .../{intermediate => }/subdomains/www/main.go | 28 +- .../subdomains/www/main_test.go | 3 +- .../httptest/main.go | 0 .../httptest/main_test.go | 14 +- .../online-visitors/main.go | 41 +- .../static/assets/js/visitors.js | 0 .../online-visitors/templates/index.html | 0 .../online-visitors/templates/other.html | 0 .../url-shortener/main.go | 188 ++++-- _examples/tutorial/url-shortener/main_test.go | 74 +++ .../url-shortener/resources/css/style.css | 0 _examples/tutorial/url-shortener/shortener.go | 4 + .../url-shortener/templates/index.html | 10 +- .../view/context-view-data/main.go | 11 +- .../context-view-data/templates/index.html | 0 .../templates/layouts/layout.html | 0 .../embedding-templates-into-app/bindata.go | 0 .../view/embedding-templates-into-app/main.go | 3 +- .../templates/hi.html | 0 _examples/view/overview/main.go | 47 ++ .../view/overview/templates/hi.html | 7 +- .../view/template_html_0/main.go | 30 +- .../view/template_html_0/templates/hi.html | 0 .../view/template_html_1/main.go | 3 +- .../template_html_1/templates/layout.html | 0 .../template_html_1/templates/mypage.html | 0 .../view/template_html_2/README.md | 0 .../view/template_html_2/main.go | 7 +- .../templates/layouts/layout.html | 0 .../templates/layouts/mylayout.html | 0 .../view/template_html_2/templates/page1.html | 0 .../templates/partials/page1_partial1.html | 0 .../view/template_html_3/main.go | 21 +- .../view/template_html_3/templates/page.html | 0 .../view/template_html_4/hosts | 4 +- .../view/template_html_4/main.go | 20 +- .../view/template_html_4/templates/page.html | 0 cache/AUTHORS | 5 + cache/LICENSE | 6 +- cache/README.md | 7 + .../_examples/simple}/main.go | 4 +- cache/cache.go | 23 +- cache/cache_test.go | 1 - cache/cfg/cfg.go | 4 - cache/client/client.go | 4 - cache/client/handler.go | 11 +- cache/client/response_recorder.go | 4 - cache/client/rule/chained.go | 4 - cache/client/rule/conditional.go | 4 - cache/client/rule/header.go | 4 - cache/client/rule/not_satisfied.go | 4 - cache/client/rule/rule.go | 4 - cache/client/rule/satisfied.go | 4 - cache/client/rule/validator.go | 4 - cache/client/ruleset.go | 9 +- cache/client/utils.go | 4 - cache/entry/entry.go | 4 - cache/entry/response.go | 4 - cache/entry/util.go | 4 - cache/ruleset/ruleset.go | 4 - cache/uri/uribuilder.go | 4 - configuration.go | 107 +++- context.go | 35 ++ context/{framework.go => application.go} | 20 +- context/configuration.go | 15 +- context/context.go | 254 ++++---- context/gzip_response_writer.go | 6 +- context/handler.go | 6 +- context/pool.go | 4 - context/render_options.go | 39 -- context/response_recorder.go | 4 - context/response_writer.go | 8 +- context/transaction.go | 4 - core/errors/errors.go | 99 ++- core/errors/errors_test.go | 49 +- core/errors/reporter.go | 141 +++++ core/errors/reporter_test.go | 26 + core/handlerconv/from_std.go | 10 +- core/host/proxy.go | 8 +- core/host/scheduler.go | 153 ----- core/host/scheduler_test.go | 80 --- core/host/supervisor.go | 189 +++--- core/host/supervisor_task_example_test.go | 118 ++++ core/host/supervisor_test.go | 12 +- core/host/task.go | 147 ++--- core/host/task_banner.go | 27 - core/host/task_example_test.go | 44 -- core/host/task_interrupt.go | 30 - core/host/world.go | 61 ++ core/logger/dev.go | 50 -- core/logger/logger.go | 50 -- core/logger/logger_test.go | 18 - core/logger/noop.go | 9 - core/memstore/memstore.go | 7 - core/{nettools => netutil}/addr.go | 29 +- core/{nettools => netutil}/addr_test.go | 6 +- core/{nettools => netutil}/server.go | 12 +- core/{nettools => netutil}/tcp.go | 6 +- core/router/api_builder.go | 169 +++-- core/router/fs.go | 73 +-- core/router/handler.go | 27 +- core/router/macro.go | 4 - core/router/macro/LICENSE | 27 - core/router/macro/interpreter/ast/ast.go | 4 - core/router/macro/interpreter/lexer/lexer.go | 4 - .../macro/interpreter/lexer/lexer_test.go | 4 - .../router/macro/interpreter/parser/parser.go | 4 - .../macro/interpreter/parser/parser_test.go | 4 - core/router/macro/interpreter/token/token.go | 4 - core/router/macro/macro.go | 6 +- core/router/macro/macro_test.go | 6 +- core/router/macro/template.go | 4 - core/router/node/node.go | 49 +- core/router/party.go | 55 +- core/router/path.go | 157 ++--- core/router/path_test.go | 29 + core/router/route.go | 10 +- core/router/router.go | 4 - core/router/{spa.go => router_spa_wrapper.go} | 2 +- core/router/status.go | 6 +- doc.go | 389 ++++++++++-- httptest/LICENSE | 52 -- httptest/httptest.go | 11 +- httptest/status.go | 73 +++ iris.go | 474 ++++++++------ iris_deprecated.go | 211 ------- middleware/README.md | 49 ++ middleware/basicauth/basicauth.go | 8 +- middleware/basicauth/basicauth_test.go | 67 -- middleware/basicauth/config.go | 4 - middleware/i18n/i18n.go | 21 +- middleware/logger/config.go | 4 - middleware/logger/logger.go | 8 +- middleware/pprof/pprof.go | 6 +- middleware/recover/recover.go | 19 +- sessions/AUTHORS | 5 + sessions/LICENSE | 6 +- sessions/README.md | 11 + .../_examples}/database/main.go | 18 +- .../_examples}/flash-messages/main.go | 16 +- .../_examples}/overview/main.go | 14 +- .../_examples}/securecookie/main.go | 23 +- sessions/_examples/securecookie/main_test.go | 105 ++++ .../_examples}/standalone/main.go | 26 +- sessions/config.go | 8 +- sessions/cookie.go | 27 +- sessions/database.go | 6 +- sessions/provider.go | 19 +- sessions/session.go | 98 ++- sessions/sessiondb/README.md | 29 - sessions/sessiondb/redis/database.go | 12 +- sessions/sessiondb/redis/service/config.go | 31 +- sessions/sessiondb/redis/service/service.go | 11 +- sessions/sessions.go | 120 ++-- sessions/sessions_test.go | 194 ++++++ typescript/AUTHORS | 5 + typescript/LICENSE | 8 +- typescript/README.md | 9 + typescript/_examples/editor/main.go | 34 + .../_examples/editor}/www/index.html | 2 +- .../_examples/editor}/www/scripts/app.ts | 2 +- .../editor}/www/scripts/tsconfig.json | 0 .../_examples}/typescript/main.go | 14 +- .../_examples}/typescript/www/index.html | 2 +- .../_examples}/typescript/www/scripts/app.ts | 2 +- typescript/config.go | 11 +- typescript/editor/LICENSE | 51 -- typescript/editor/config.go | 4 - typescript/editor/editor.go | 84 ++- typescript/npm/exec.go | 4 - typescript/npm/npm.go | 16 +- typescript/typescript.go | 190 +++--- view/LICENSE | 111 ---- view/amber.go | 4 - view/django.go | 4 - view/engine.go | 15 +- view/funcs.go | 4 - view/handlebars.go | 4 - view/html.go | 49 +- view/pug.go | 4 - view/view.go | 9 +- websocket/AUTHORS | 5 + websocket/LICENSE | 31 +- websocket/README.md | 11 + .../_examples/chat}/main.go | 60 +- .../_examples/chat}/websockets.html | 0 .../_examples}/connectionlist/main.go | 40 +- .../connectionlist/static/js/chat.js | 0 .../connectionlist/templates/client.html | 2 +- .../_examples}/custom-go-client/main.go | 12 +- .../_examples}/native-messages/main.go | 16 +- .../native-messages/static/js/chat.js | 0 .../native-messages/templates/client.html | 0 .../_examples}/secure/main.go | 29 +- .../_examples}/secure/static/js/chat.js | 0 .../_examples}/secure/templates/client.html | 2 +- websocket/client.go | 35 +- websocket/client.ts | 18 +- websocket/config.go | 38 +- websocket/connection.go | 32 +- websocket/emitter.go | 12 +- websocket/message.go | 14 - websocket/server.go | 192 +++--- websocket/websocket.go | 133 ++-- 344 files changed, 4842 insertions(+), 5174 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/CODEOWNERS create mode 100644 ACQUIRED_HISTORY.md create mode 100644 AUTHORS delete mode 100644 CODE-OF-CONDUCT.md create mode 100644 Dockerfile create mode 100644 Dockerfile.build delete mode 100644 Gopkg.lock delete mode 100644 Gopkg.toml delete mode 100644 _examples/advanced/url-shortener/shortener.go create mode 100644 _examples/authentication/README.md rename _examples/{beginner => authentication}/basicauth/main.go (95%) create mode 100644 _examples/authentication/basicauth/main_test.go rename _examples/{intermediate => authentication}/oauth2/main.go (96%) rename _examples/{intermediate => authentication}/oauth2/templates/index.html (100%) rename _examples/{intermediate => authentication}/oauth2/templates/user.html (100%) delete mode 100644 _examples/beginner/e-mail/main.go delete mode 100644 _examples/beginner/e-mail/templates/mail_body.html delete mode 100644 _examples/beginner/favicon/static/favicons/iris_favicon_48_48.ico delete mode 100644 _examples/beginner/file-server/basic/assets/favicon.ico delete mode 100644 _examples/beginner/file-server/embedding-files-into-app/assets/favicon.ico delete mode 100644 _examples/beginner/hello-world/main.go delete mode 100644 _examples/beginner/listening/listen-unix/main.go delete mode 100644 _examples/beginner/write-json/main.go rename _examples/{beginner/configuration/basic => configuration/from-configuration-structure}/main.go (72%) rename _examples/{beginner/configuration/from-toml-file/configs/iris.tml => configuration/from-toml-file/configs/ion.tml} (89%) rename _examples/{beginner => }/configuration/from-toml-file/main.go (79%) rename _examples/{beginner/configuration/from-yaml-file/configs/iris.yml => configuration/from-yaml-file/configs/ion.yml} (100%) rename _examples/{beginner => }/configuration/from-yaml-file/main.go (79%) rename _examples/{beginner => }/configuration/functional/main.go (59%) rename _examples/{beginner => }/convert-handlers/negroni-like/main.go (82%) rename _examples/{beginner => }/convert-handlers/nethttp/main.go (73%) rename _examples/{beginner => }/file-server/basic/assets/css/main.css (100%) create mode 100644 _examples/file-server/basic/assets/favicon.ico rename _examples/{beginner => }/file-server/basic/assets/js/jquery-2.1.1.js (100%) rename _examples/{beginner => }/file-server/basic/main.go (100%) rename _examples/{beginner => }/file-server/embedding-files-into-app/assets/css/bootstrap.min.css (100%) create mode 100644 _examples/file-server/embedding-files-into-app/assets/favicon.ico rename _examples/{beginner => }/file-server/embedding-files-into-app/assets/js/jquery-2.1.1.js (100%) rename _examples/{beginner => }/file-server/embedding-files-into-app/bindata.go (95%) rename _examples/{beginner => }/file-server/embedding-files-into-app/main.go (100%) rename _examples/{beginner => }/file-server/embedding-files-into-app/main_test.go (75%) rename _examples/{beginner => file-server}/favicon/main.go (56%) create mode 100644 _examples/file-server/favicon/static/favicons/favicon.ico rename _examples/{beginner => file-server}/send-files/files/first.zip (100%) rename _examples/{beginner => file-server}/send-files/main.go (100%) rename _examples/{beginner/file-server/single-page-application => file-server/single-page-application/basic}/main.go (83%) rename _examples/{beginner/file-server/single-page-application => file-server/single-page-application/basic}/main_test.go (89%) rename _examples/{beginner/file-server/embedding-single-page-application => file-server/single-page-application/basic}/public/app.js (100%) rename _examples/{beginner/file-server/embedding-single-page-application => file-server/single-page-application/basic}/public/css/main.css (100%) rename _examples/{beginner/file-server/embedding-single-page-application => file-server/single-page-application/basic}/public/index.html (100%) rename _examples/{beginner/file-server/embedding-single-page-application => file-server/single-page-application/embedded-single-page-application}/bindata.go (95%) rename _examples/{beginner/file-server/embedding-single-page-application => file-server/single-page-application/embedded-single-page-application}/main.go (91%) rename _examples/{beginner/file-server/embedding-single-page-application => file-server/single-page-application/embedded-single-page-application}/main_test.go (85%) rename _examples/{beginner/file-server/single-page-application => file-server/single-page-application/embedded-single-page-application}/public/app.js (100%) rename _examples/{beginner/file-server/single-page-application => file-server/single-page-application/embedded-single-page-application}/public/css/main.css (100%) rename _examples/{beginner/file-server/single-page-application => file-server/single-page-application/embedded-single-page-application}/public/index.html (100%) create mode 100644 _examples/hello-world/main.go create mode 100644 _examples/hello-world/main_go19.go rename _examples/{intermediate/custom-httpserver/iris-way => http-listening/custom-httpserver/ion-way}/main.go (84%) rename _examples/{intermediate => http-listening}/custom-httpserver/multi/main.go (100%) rename _examples/{intermediate => http-listening}/custom-httpserver/std-way/main.go (89%) rename _examples/{beginner/listening => http-listening}/custom-listener/main.go (100%) create mode 100644 _examples/http-listening/custom-listener/unix-reuseport/main.go create mode 100644 _examples/http-listening/custom-listener/unix-reuseport/main_windows.go rename _examples/{intermediate => http-listening}/graceful-shutdown/custom-notifier/main.go (70%) create mode 100644 _examples/http-listening/graceful-shutdown/default-notifier/main.go rename _examples/{beginner/listening => http-listening}/listen-addr/main.go (86%) rename _examples/{beginner/listening => http-listening}/listen-letsencrypt/main.go (88%) rename _examples/{beginner/listening => http-listening}/listen-tls/main.go (100%) rename _examples/{beginner/listening => http-listening}/listen-tls/mycert.cert (100%) rename _examples/{beginner/listening => http-listening}/listen-tls/mykey.key (100%) create mode 100644 _examples/http-listening/listen-unix/main.go create mode 100644 _examples/http_request/README.md rename _examples/{beginner => http_request}/read-form/main.go (93%) rename _examples/{beginner => http_request}/read-form/templates/form.html (100%) rename _examples/{beginner => http_request}/read-json/main.go (89%) rename _examples/{beginner => http_request}/request-logger/main.go (100%) rename _examples/{beginner => http_request}/upload-files/main.go (96%) rename _examples/{beginner => http_request}/upload-files/templates/upload_form.html (100%) create mode 100644 _examples/http_responsewriter/README.md rename _examples/{beginner => http_responsewriter}/stream-writer/main.go (100%) rename _examples/{intermediate => http_responsewriter}/transactions/main.go (99%) rename _examples/{intermediate/view/overview => http_responsewriter/write-rest}/main.go (51%) delete mode 100644 _examples/intermediate/cloud-editor/main.go delete mode 100644 _examples/intermediate/graceful-shutdown/basic/main.go delete mode 100644 _examples/intermediate/graceful-shutdown/custom-host/main.go delete mode 100644 _examples/intermediate/i18n/locales/locale_el-GR.ini delete mode 100644 _examples/intermediate/sessions/password-hashing/main.go delete mode 100644 _examples/intermediate/subdomains/multi/public/assets/images/test.ico delete mode 100644 _examples/intermediate/subdomains/multi/public/upload_resources/iris_favicon_32_32.ico delete mode 100644 _examples/intermediate/websockets/overview/main.go delete mode 100644 _examples/intermediate/websockets/overview/static/js/chat.js delete mode 100644 _examples/intermediate/websockets/overview/templates/client.html rename _examples/{beginner => miscellaneous}/file-logger/main.go (80%) create mode 100644 _examples/miscellaneous/i18n/locales/locale_el-GR.ini rename _examples/{intermediate => miscellaneous}/i18n/locales/locale_en-US.ini (100%) rename _examples/{intermediate => miscellaneous}/i18n/locales/locale_zh-CN.ini (100%) rename _examples/{intermediate => miscellaneous}/i18n/main.go (82%) create mode 100644 _examples/miscellaneous/i18n/main_test.go rename _examples/{beginner => miscellaneous}/pprof/main.go (100%) rename _examples/{beginner => miscellaneous}/recover/main.go (100%) rename _examples/{beginner => }/overview/main.go (89%) rename _examples/{beginner => }/overview/views/users/create_verification.html (100%) rename _examples/{beginner => }/overview/views/users/profile.html (100%) rename _examples/{beginner => }/routing/basic/main.go (95%) rename _examples/{intermediate => routing}/custom-context/method-overriding/main.go (80%) rename _examples/{intermediate => routing}/custom-context/method-overriding/view/hi.html (100%) create mode 100644 _examples/routing/custom-context/new-implementation/main.go create mode 100644 _examples/routing/custom-context/new-implementation/main_test.go rename _examples/{beginner => }/routing/custom-wrapper/main.go (96%) rename _examples/{beginner => }/routing/custom-wrapper/main_test.go (94%) rename _examples/{beginner => }/routing/custom-wrapper/public/app.js (100%) rename _examples/{beginner => }/routing/custom-wrapper/public/css/main.css (100%) rename _examples/{beginner => }/routing/custom-wrapper/public/index.html (100%) rename _examples/{beginner => }/routing/dynamic-path/main.go (95%) rename _examples/{beginner => routing}/http-errors/main.go (100%) rename _examples/{beginner => }/routing/main.go (53%) create mode 100644 _examples/routing/main_test.go rename _examples/{beginner => }/routing/overview/main.go (90%) rename _examples/{beginner => }/routing/reverse/main.go (84%) rename _examples/{intermediate => routing}/route-state/main.go (78%) rename _examples/{intermediate => }/subdomains/multi/hosts (92%) rename _examples/{intermediate => }/subdomains/multi/main.go (98%) create mode 100644 _examples/subdomains/multi/public/assets/images/test.ico create mode 100644 _examples/subdomains/multi/public/upload_resources/favicon.ico rename _examples/{intermediate => }/subdomains/single/hosts (92%) rename _examples/{intermediate => }/subdomains/single/main.go (100%) rename _examples/{intermediate => }/subdomains/wildcard/hosts (93%) rename _examples/{intermediate => }/subdomains/wildcard/main.go (100%) rename _examples/{intermediate => }/subdomains/www/hosts (87%) rename _examples/{intermediate => }/subdomains/www/main.go (62%) rename _examples/{intermediate => }/subdomains/www/main_test.go (95%) rename _examples/{intermediate => testing}/httptest/main.go (100%) rename _examples/{intermediate => testing}/httptest/main_test.go (55%) rename _examples/{advanced => tutorial}/online-visitors/main.go (88%) rename _examples/{advanced => tutorial}/online-visitors/static/assets/js/visitors.js (100%) rename _examples/{advanced => tutorial}/online-visitors/templates/index.html (100%) rename _examples/{advanced => tutorial}/online-visitors/templates/other.html (100%) rename _examples/{advanced => tutorial}/url-shortener/main.go (65%) create mode 100644 _examples/tutorial/url-shortener/main_test.go rename _examples/{advanced => tutorial}/url-shortener/resources/css/style.css (100%) create mode 100644 _examples/tutorial/url-shortener/shortener.go rename _examples/{advanced => tutorial}/url-shortener/templates/index.html (62%) rename _examples/{intermediate => }/view/context-view-data/main.go (77%) rename _examples/{intermediate => }/view/context-view-data/templates/index.html (100%) rename _examples/{intermediate => }/view/context-view-data/templates/layouts/layout.html (100%) rename _examples/{intermediate => }/view/embedding-templates-into-app/bindata.go (100%) rename _examples/{intermediate => }/view/embedding-templates-into-app/main.go (83%) rename _examples/{intermediate => }/view/embedding-templates-into-app/templates/hi.html (100%) create mode 100644 _examples/view/overview/main.go rename _examples/{intermediate => }/view/overview/templates/hi.html (64%) rename _examples/{intermediate => }/view/template_html_0/main.go (50%) rename _examples/{intermediate => }/view/template_html_0/templates/hi.html (100%) rename _examples/{intermediate => }/view/template_html_1/main.go (86%) rename _examples/{intermediate => }/view/template_html_1/templates/layout.html (100%) rename _examples/{intermediate => }/view/template_html_1/templates/mypage.html (100%) rename _examples/{intermediate => }/view/template_html_2/README.md (100%) rename _examples/{intermediate => }/view/template_html_2/main.go (90%) rename _examples/{intermediate => }/view/template_html_2/templates/layouts/layout.html (100%) rename _examples/{intermediate => }/view/template_html_2/templates/layouts/mylayout.html (100%) rename _examples/{intermediate => }/view/template_html_2/templates/page1.html (100%) rename _examples/{intermediate => }/view/template_html_2/templates/partials/page1_partial1.html (100%) rename _examples/{intermediate => }/view/template_html_3/main.go (63%) rename _examples/{intermediate => }/view/template_html_3/templates/page.html (100%) rename _examples/{intermediate => }/view/template_html_4/hosts (92%) rename _examples/{intermediate => }/view/template_html_4/main.go (68%) rename _examples/{intermediate => }/view/template_html_4/templates/page.html (100%) create mode 100644 cache/AUTHORS create mode 100644 cache/README.md rename {_examples/intermediate/cache-markdown => cache/_examples/simple}/main.go (96%) create mode 100644 context.go rename context/{framework.go => application.go} (62%) delete mode 100644 context/render_options.go create mode 100644 core/errors/reporter.go create mode 100644 core/errors/reporter_test.go delete mode 100644 core/host/scheduler.go delete mode 100644 core/host/scheduler_test.go create mode 100644 core/host/supervisor_task_example_test.go delete mode 100644 core/host/task_banner.go delete mode 100644 core/host/task_example_test.go delete mode 100644 core/host/task_interrupt.go create mode 100644 core/host/world.go delete mode 100644 core/logger/dev.go delete mode 100644 core/logger/logger.go delete mode 100644 core/logger/logger_test.go delete mode 100644 core/logger/noop.go rename core/{nettools => netutil}/addr.go (91%) rename core/{nettools => netutil}/addr_test.go (86%) rename core/{nettools => netutil}/server.go (76%) rename core/{nettools => netutil}/tcp.go (96%) delete mode 100644 core/router/macro/LICENSE create mode 100644 core/router/path_test.go rename core/router/{spa.go => router_spa_wrapper.go} (97%) delete mode 100644 httptest/LICENSE create mode 100644 httptest/status.go delete mode 100644 iris_deprecated.go create mode 100644 middleware/README.md delete mode 100644 middleware/basicauth/basicauth_test.go create mode 100644 sessions/AUTHORS create mode 100644 sessions/README.md rename {_examples/intermediate/sessions => sessions/_examples}/database/main.go (78%) rename {_examples/intermediate/sessions => sessions/_examples}/flash-messages/main.go (69%) rename {_examples/intermediate/sessions => sessions/_examples}/overview/main.go (67%) rename {_examples/intermediate/sessions => sessions/_examples}/securecookie/main.go (76%) create mode 100644 sessions/_examples/securecookie/main_test.go rename {_examples/intermediate/sessions => sessions/_examples}/standalone/main.go (85%) delete mode 100644 sessions/sessiondb/README.md create mode 100644 sessions/sessions_test.go create mode 100644 typescript/AUTHORS create mode 100644 typescript/README.md create mode 100644 typescript/_examples/editor/main.go rename {_examples/intermediate/cloud-editor => typescript/_examples/editor}/www/index.html (69%) rename {_examples/intermediate/cloud-editor => typescript/_examples/editor}/www/scripts/app.ts (83%) rename {_examples/intermediate/cloud-editor => typescript/_examples/editor}/www/scripts/tsconfig.json (100%) rename {_examples/intermediate => typescript/_examples}/typescript/main.go (73%) rename {_examples/intermediate => typescript/_examples}/typescript/www/index.html (69%) rename {_examples/intermediate => typescript/_examples}/typescript/www/scripts/app.ts (83%) delete mode 100644 typescript/editor/LICENSE delete mode 100644 view/LICENSE create mode 100644 websocket/AUTHORS create mode 100644 websocket/README.md rename {_examples/intermediate/websockets/ridiculous-simple => websocket/_examples/chat}/main.go (63%) rename {_examples/intermediate/websockets/ridiculous-simple => websocket/_examples/chat}/websockets.html (100%) rename {_examples/intermediate/websockets => websocket/_examples}/connectionlist/main.go (58%) rename {_examples/intermediate/websockets => websocket/_examples}/connectionlist/static/js/chat.js (100%) rename {_examples/intermediate/websockets => websocket/_examples}/connectionlist/templates/client.html (89%) rename {_examples/intermediate/websockets => websocket/_examples}/custom-go-client/main.go (95%) rename {_examples/intermediate/websockets => websocket/_examples}/native-messages/main.go (74%) rename {_examples/intermediate/websockets => websocket/_examples}/native-messages/static/js/chat.js (100%) rename {_examples/intermediate/websockets => websocket/_examples}/native-messages/templates/client.html (100%) rename {_examples/intermediate/websockets => websocket/_examples}/secure/main.go (90%) rename {_examples/intermediate/websockets => websocket/_examples}/secure/static/js/chat.js (100%) rename {_examples/intermediate/websockets => websocket/_examples}/secure/templates/client.html (88%) diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..05fd0d8e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +*.go linguist-language=Go +vendor/* linguist-vendored +_examples/* linguist-documentation \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..6ca7098e --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,6 @@ +# These owners will be the default owners for everything in the repo. +* @kataras +*.go @hiveminded @kataras +*.md @hiveminded @kataras +*.html @hiveminded @kataras +*.css @hiveminded @kataras \ No newline at end of file diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 61f7a35c..3b94bfd1 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1 +1,74 @@ -Please navigate through https://github.com/iris-contrib/community-board first to read how you can contribute. You're awesome! \ No newline at end of file +# 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/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index b135792e..629592f5 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,5 +1,2 @@ -**Do not create** issues or proposals neither request features **here**. - -Navigate to the https://github.com/iris-contrib/community-board instead. - -[FAQ](https://github.com/iris-contrib/community-board#community-board) \ No newline at end of file +Documentation for the _iris_ project can be found at +. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9ee434be..5286e042 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,3 @@ -I'd love to see more contributions! +We'd love to see contributions!!! -Please read [how to create a Proposal](https://github.com/iris-contrib/community-board#creating-a-proposal) first, PR may be rejected if it's not designed as it should. - -If you are interested in contributing to the Iris project, please take a time to read and understand the [Code of Conduct](https://github.com/iris-contrib/community-board/blob/master/CODE-OF-CONDUCT.md) before submitting your [PR](https://github.com/kataras/iris/pulls), this is how we make Go great. - -Thanks! \ No newline at end of file +Please attach an [issue](http://support.iris-go.com) link which your PR solves otherwise your work may be rejected. \ No newline at end of file diff --git a/.gitignore b/.gitignore index d8f195fa..e69de29b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +0,0 @@ -# my own configuration for vs code, my lovely editor, if someone of you want this, contact with me -.vscode -# turn off these for now -specs \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index d26da8b3..52b6cb81 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,22 +4,30 @@ os: - osx go: - go1.8 + - tip go_import_path: github.com/kataras/iris install: - - go get ./... -# install test dependencies -# - go get golang.org/x/tools/cmd/cover -# - go get -v github.com/axw/gocov -# - go install github.com/axw/gocov/gocov + - go get ./... # for iris-contrib/httpexpect script: - # - gocov test | gocov report - # the result of gocov is invalid because it tests the vendor too, - # which its tests are removed to reduce the dl size. - # When I'll push my internal tests to github I'll do it - # to do the test coverage all folders except vendor. - # For now keep it commented. - # - cd ./_examples - # - go get ./... - # - go test -v -cover ./... - # - cd ./... + - go test -v -cover ./... +after_script: + # examples + - cd ./_examples + - go get ./... + - go test -v -cover ./... + # cache examples + - cd ../cache/_examples + - go get ./... + - go test -v -cover ./... + # sessions examples + - cd ../../sessions/_examples + - go get ./... + - go test -v -cover ./... + # websocket examples + - cd ../../websocket/_examples + - go get ./... + - go test -v -cover ./... + # typescript examples + - cd ../../typescript/_examples + - go get ./... - go test -v -cover ./... \ No newline at end of file diff --git a/ACQUIRED_HISTORY.md b/ACQUIRED_HISTORY.md new file mode 100644 index 00000000..10e911cd --- /dev/null +++ b/ACQUIRED_HISTORY.md @@ -0,0 +1,89 @@ +# 10 July 2017 + +## 📈 One and a half years with Iris and You... + +- 7070 github stars +- 749 github forks +- 1m total views at its documentation +- ~800$ at donations (there're a lot for a golang open-source project, thanks to you) +- ~550 reported bugs fixed +- ~30 community feature requests have been implemented + +## 🔥 Reborn + +As you may have heard I have huge responsibilities on my new position at Dubai nowdays, therefore I don't have the needed time to work on this project anymore. + +After almost a month of negotiations and searching I succeed to find a decent software engineer to continue my work on the open source community. + +The leadership of this, open-source, repository was transfered to [hiveminded](https://github.com/hiveminded). + +These types of projects need heart and sacrifices to continue offer the best developer experience like a paid software, please do support him as you did with me! + +# 02 July 2017 + +### DEPRECATED + +Iris has been acquired so development is up to the community, there are two active iris-based communities so far. + +Use one of these projects instead: + +https://github.com/get-ion/ion + +**Ion is a fast, simple and efficient micro web framework for Go. It provides a beautifully expressive and easy to use foundation for your next website, API, or distributed app.** + +- a bit faster than Iris version 7, based on `ab` +- stable api +- more examples +- sessions, websockets, typescript and cloud editor(fixed) on different packages +- test cov, including examples +- slack bot for support automation +- has a FAQ page which is part of the gitbook.com beta program +- central issue portal +- HuHu supported + +https://github.com/go-siris/siris + +**A fast, cross-platform and efficient web framework with robust set of well-designed features, written entirely in Go.** + +- three maintainers +- plan to stabilize api, no unneeded changes +- plan to increase test-coverage +- plan to add more middlewares and examples + + +> If your team's project is missing from this list, please contact with me. + +# 17 June 2017 + +### IRIS HAS BEEN ACQUIRED + +Iris project has been acquired by a Dubai-based startup. + +Both sides agree that every related public data should remain open for at least 30 days. + +After the period of 30 days, company has the proprietary rights to delete or transfer this repository and all its related public data forever without any warnings. + +The company may or may not reveal its true identity to the public. + +Transaction of the public domains still in-progress: + +- http://iris-go.com +- https://kataras.rocket.chat/channel/iris + +View-accessed users can clone the current state of the project's public repositories and use without any warranties. + +From now on, Original Author owns a high position to the company's table. + +At any circumstances, + +Original Author keeps the creation rights. + +### About the future of Iris + +Clone the repository today because if I can't find a new lead maintainer for the [v7.2](https://github.com/kataras/iris-v7-29d) you, as community, will have to find a way to communicate about its future, the name "iris go" was taken by the company too, so it will be nice if the future main contributor change its name too, if you don't do it I will not beat you but I don't know the full company's law-plan for this, yet. + +All donators, without any exception, will have my support for at least 6 months (for all iris versions), we have a private room at the [chat](https://kataras.rocket.chat/channel/iris). + +Don't worry **I will not let you down**, we're trying to find a decent open-source contributor to continue the Iris' open-source codebase. I'm already in touch with some good gophers but **If you're willing to maintain this project** please [send](#contact) me details about your experience, general bio and your github username. + +**I am really thankful for all of your support to me and the community, all donations, all bug reports, all comments without any exception. I did proceeded with all my physical abilities so far but unfortunately there weren't enough for my survivor. I'm really sorry if the latest news made iris open-source community disappointed but you have to see things from my point view, I was one step before bankruptcy, I had no other choice but accept the offer.** \ No newline at end of file diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..20107111 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,5 @@ +# This is the official list of Iris authors for copyright +# purposes. + +Gerasimos Maropoulos +Bill Qeras, Jr. \ No newline at end of file diff --git a/CODE-OF-CONDUCT.md b/CODE-OF-CONDUCT.md deleted file mode 100644 index 0df8933c..00000000 --- a/CODE-OF-CONDUCT.md +++ /dev/null @@ -1,41 +0,0 @@ -# Iris Open Source Code of Conduct - -This code of conduct outlines expectations for participation in kataras-managed open source communities, as well as steps for reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all. People violating this code of conduct may be banned from the community. - -Our open source communities strive to: - -* **Be friendly and patient**: Remember you might not be communicating in someone else's primary spoken or programming language, and others may not have your level of understanding. -* **Be welcoming**: Our communities welcome and support people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, color, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability. -* **Be respectful**: We are a world-wide community of professionals, and we conduct ourselves professionally. Disagreement is no excuse for poor behavior and poor manners. Disrespectful and unacceptable behavior includes, but is not limited to: - * Violent threats or language. - * Discriminatory or derogatory jokes and language. - * Posting sexually explicit or violent material. - * Posting, or threatening to post, people's personally identifying information ("doxing"). - * Insults, especially those using discriminatory terms or slurs. - * Behavior that could be perceived as sexual attention. - * Advocating for or encouraging any of the above behaviors. -* **Understand disagreements**: Disagreements, both social and technical, are useful learning opportunities. Seek to understand the other viewpoints and resolve differences constructively. -* This code is not exhaustive or complete. It serves to capture our common understanding of a productive, collaborative environment. We expect the code to be followed in spirit as much as in the letter. - -## Scope - -This code of conduct applies to all repos and communities for kataras-managed open source projects regardless of whether or not the repo explicitly calls out its use of this code. The code also applies in public spaces when an individual is representing a project or its community. Examples 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. - - -## Reporting Code of Conduct Issues - -We encourage all communities to resolve issues on their own whenever possible. This builds a broader and deeper understanding and ultimately a healthier interaction. In the event that an issue cannot be resolved locally, please feel free to report your concerns by mail to kataras2006@hotmail.com or kataras@tutanota.com (secure way). - -In your report please include: - -* Your contact information. -* Names (real, usernames or pseudonyms) of any individuals involved. If there are additional witnesses, please include them as well. -* Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public chat log), please include a link or attachment. -* Any additional information that may be helpful. - - -All reports will be reviewed by [kataras](https://github.com/kataras) and will result in a response that is deemed necessary and appropriate to the circumstances. Where additional perspectives are needed, I may seek insight from others with relevant expertise or experience. The confidentiality of the person reporting the incident will be kept at all times. Involved parties are never part of the review team. - -Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual engages in **unacceptable behavior**, the review team may take any action they deem appropriate, **including a permanent ban from the community**. - -This code of conduct is based on the [template](http://todogroup.org/opencodeofconduct) established by the [TODO Group](http://todogroup.org/) and used by numerous other large communities (e.g., [Facebook](https://code.facebook.com/pages/876921332402685/open-source-code-of-conduct), [Yahoo](https://yahoo.github.io/codeofconduct), [Twitter](https://engineering.twitter.com/opensource/code-of-conduct), [GitHub](http://todogroup.org/opencodeofconduct/#opensource@github.com)) and the Scope section from the [Contributor Covenant version 1.4](http://contributor-covenant.org/version/1/4/). \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..580c3b52 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,5 @@ +FROM irisgo/cloud-native-go:latest + +ENV APPSOURCES /go/src/github.com/iris-contrib/cloud-native-go + +RUN ${APPSOURCES}/cloud-native-go \ No newline at end of file diff --git a/Dockerfile.build b/Dockerfile.build new file mode 100644 index 00000000..342b9c39 --- /dev/null +++ b/Dockerfile.build @@ -0,0 +1,12 @@ +FROM golang:1.8.3-alpine + +RUN apk update && apk upgrade && apk add --no-cache bash git +RUN go get github.com/iris-contrib/cloud-native-go + +ENV SOURCES /go/src/github.com/iris-contrib/cloud-native-go +# COPY . ${SOURCES} + +RUN cd ${SOURCES} $$ CGO_ENABLED=0 go build + +ENTRYPOINT cloud-native-go +EXPOSE 8080 \ No newline at end of file diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index 321ae898..00000000 --- a/Gopkg.lock +++ /dev/null @@ -1,303 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - name = "github.com/BurntSushi/toml" - packages = ["."] - revision = "b26d9c308763d68093482582cea63d69be07a0f0" - -[[projects]] - branch = "master" - name = "github.com/Joker/jade" - packages = ["."] - revision = "35b3f5bdbcc920cd31f4870536dbc63be8530541" - -[[projects]] - branch = "master" - name = "github.com/Unknwon/i18n" - packages = ["."] - revision = "8372b908b5876d26cfa46a85fc4851b981dad102" - -[[projects]] - branch = "master" - name = "github.com/ajg/form" - packages = ["."] - revision = "523a5da1a92f01b01f840b61689c0340a0243532" - -[[projects]] - branch = "master" - name = "github.com/aymerick/raymond" - packages = [".","ast","lexer","parser"] - revision = "72acac2207479d21dd45898c2a4264246c818148" - -[[projects]] - name = "github.com/davecgh/go-spew" - packages = ["spew"] - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" - -[[projects]] - branch = "master" - name = "github.com/eknkc/amber" - packages = [".","parser"] - revision = "f0d8fdb67f9f4a2c0d02fb6ce4830b8b6754de10" - -[[projects]] - branch = "master" - name = "github.com/esemplastic/unis" - packages = [".","logger"] - revision = "6e30ed034e8cb31227b24057d030fdd0261bcfc6" - -[[projects]] - branch = "master" - name = "github.com/fatih/structs" - packages = ["."] - revision = "74a29b9fac7397d933f47e33eba94d2d83a464a2" - -[[projects]] - branch = "master" - name = "github.com/flosch/pongo2" - packages = ["."] - revision = "1d0f0d3af150c4a65dfd424d742f7374819e7d29" - -[[projects]] - name = "github.com/garyburd/redigo" - packages = ["internal","redis"] - revision = "433969511232c397de61b1442f9fd49ec06ae9ba" - version = "v1.1.0" - -[[projects]] - branch = "master" - name = "github.com/gavv/monotime" - packages = ["."] - revision = "47d58efa69556a936a3c15eb2ed42706d968ab01" - -[[projects]] - branch = "master" - name = "github.com/getlantern/context" - packages = ["."] - revision = "624d99b1798d7c5375ea1d3ca4c5b04d58f7c775" - -[[projects]] - branch = "master" - name = "github.com/getlantern/errors" - packages = ["."] - revision = "99fa440517e8f3d1e4cd8d6dbed6b41f4c1ed3d6" - -[[projects]] - branch = "master" - name = "github.com/getlantern/filepersist" - packages = ["."] - revision = "c5f0cd24e7991579ba6f5f1bd20a1ad2c9f06cd4" - -[[projects]] - branch = "master" - name = "github.com/getlantern/golog" - packages = ["."] - revision = "cca714f7feb5df8e455f409b549d384441ac4578" - -[[projects]] - branch = "master" - name = "github.com/getlantern/hex" - packages = ["."] - revision = "083fba3033ad473db3dd31c9bb368473d37581a7" - -[[projects]] - branch = "master" - name = "github.com/getlantern/hidden" - packages = ["."] - revision = "d52a649ab33af200943bb599898dbdcfdbc94cb7" - -[[projects]] - branch = "master" - name = "github.com/getlantern/ops" - packages = ["."] - revision = "b70875f5d689a9438bca72aefd7142a2af889b18" - -[[projects]] - branch = "master" - name = "github.com/getlantern/stack" - packages = ["."] - revision = "02f928aad224fbccd50d66edd776fc9d1e9f2f2b" - -[[projects]] - branch = "master" - name = "github.com/google/go-querystring" - packages = ["query"] - revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a" - -[[projects]] - name = "github.com/gorilla/websocket" - packages = ["."] - revision = "3ab3a8b8831546bd18fd182c20687ca853b2bb13" - version = "v1.1.0" - -[[projects]] - name = "github.com/imdario/mergo" - packages = ["."] - revision = "3e95a51e0639b4cf372f2ccf74c86749d747fbdc" - version = "0.2.2" - -[[projects]] - branch = "master" - name = "github.com/imkira/go-interpol" - packages = ["."] - revision = "5accad8134979a6ac504d456a6c7f1c53da237ca" - -[[projects]] - branch = "master" - name = "github.com/iris-contrib/httpexpect" - packages = ["."] - revision = "ed44d3c477393b96a598a38730f128dffee33068" - -[[projects]] - branch = "master" - name = "github.com/juju/errors" - packages = ["."] - revision = "8234c829496aaeae67c5c71235a2cbcc289e960a" - -[[projects]] - branch = "master" - name = "github.com/klauspost/compress" - packages = ["flate","gzip"] - revision = "f3dce52e0576655d55fd69e74b63da96ad1108f3" - -[[projects]] - branch = "master" - name = "github.com/klauspost/cpuid" - packages = ["."] - revision = "09cded8978dc9e80714c4d85b0322337b0a1e5e0" - -[[projects]] - branch = "master" - name = "github.com/microcosm-cc/bluemonday" - packages = ["."] - revision = "e79763773ab6222ca1d5a7cbd9d62d83c1f77081" - -[[projects]] - branch = "master" - name = "github.com/monoculum/formam" - packages = ["."] - revision = "334e05a3c7f95d500715d7c0405937ac933b28cb" - -[[projects]] - branch = "master" - name = "github.com/moul/http2curl" - packages = ["."] - revision = "4e24498b31dba4683efb9d35c1c8a91e2eda28c8" - -[[projects]] - name = "github.com/pmezard/go-difflib" - packages = ["difflib"] - revision = "792786c7400a136282c1664665ae0a8db921c6c2" - version = "v1.0.0" - -[[projects]] - branch = "master" - name = "github.com/russross/blackfriday" - packages = ["."] - revision = "0ba0f2b6ed7c475a92e4df8641825cb7a11d1fa3" - -[[projects]] - branch = "master" - name = "github.com/satori/go.uuid" - packages = ["."] - revision = "5bf94b69c6b68ee1b541973bb8e1144db23a194b" - -[[projects]] - branch = "master" - name = "github.com/sergi/go-diff" - packages = ["diffmatchpatch"] - revision = "feef008d51ad2b3778f85d387ccf91735543008d" - -[[projects]] - branch = "master" - name = "github.com/stretchr/testify" - packages = ["assert","require"] - revision = "f6abca593680b2315d2075e0f5e2a9751e3f431a" - -[[projects]] - branch = "master" - name = "github.com/valyala/bytebufferpool" - packages = ["."] - revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7" - -[[projects]] - branch = "master" - name = "github.com/xeipuuv/gojsonpointer" - packages = ["."] - revision = "6fe8760cad3569743d51ddbb243b26f8456742dc" - -[[projects]] - branch = "master" - name = "github.com/xeipuuv/gojsonreference" - packages = ["."] - revision = "e02fc20de94c78484cd5ffb007f8af96be030a45" - -[[projects]] - branch = "master" - name = "github.com/xeipuuv/gojsonschema" - packages = ["."] - revision = "0c8571ac0ce161a5feb57375a9cdf148c98c0f70" - -[[projects]] - branch = "master" - name = "github.com/yalp/jsonpath" - packages = ["."] - revision = "31a79c7593bb93eb10b163650d4a3e6ca190e4dc" - -[[projects]] - name = "github.com/yudai/gojsondiff" - packages = [".","formatter"] - revision = "7b1b7adf999dab73a6eb02669c3d82dbb27a3dd6" - version = "1.0.0" - -[[projects]] - branch = "master" - name = "github.com/yudai/golcs" - packages = ["."] - revision = "ecda9a501e8220fae3b4b600c3db4b0ba22cfc68" - -[[projects]] - branch = "master" - name = "golang.org/x/crypto" - packages = ["acme","acme/autocert"] - revision = "e1a4589e7d3ea14a3352255d04b6f1a418845e5e" - -[[projects]] - branch = "master" - name = "golang.org/x/net" - packages = ["html","html/atom","idna","publicsuffix"] - revision = "e4fa1c5465ad6111f206fc92186b8c83d64adbe1" - -[[projects]] - branch = "master" - name = "golang.org/x/text" - packages = ["internal/gen","internal/triegen","internal/ucd","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"] - revision = "ccbd3f7822129ff389f8ca4858a9b9d4d910531c" - -[[projects]] - name = "gopkg.in/ini.v1" - packages = ["."] - revision = "d3de07a94d22b4a0972deb4b96d790c2c0ce8333" - version = "v1.28.0" - -[[projects]] - branch = "v2" - name = "gopkg.in/yaml.v2" - packages = ["."] - revision = "cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b" - -[[projects]] - branch = "master" - name = "github.com/PuerkitoBio/goquery" - packages = ["."] - revision = "2dc93891ab3bcc1dbc2cbf9bc6376c37f6e6f289" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - inputs-digest = "415eba3367a3b497ce9c74414ba802d5902d5b7fefc7dfc99cff6af102ec40fe" - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index 2b2926c3..00000000 --- a/Gopkg.toml +++ /dev/null @@ -1,179 +0,0 @@ - -## Gopkg.toml example (these lines may be deleted) - -## "metadata" defines metadata about the project that could be used by other independent -## systems. The metadata defined here will be ignored by dep. -# [metadata] -# key1 = "value that convey data to other systems" -# system1-data = "value that is used by a system" -# system2-data = "value that is used by another system" - -## "required" lists a set of packages (not projects) that must be included in -## Gopkg.lock. This list is merged with the set of packages imported by the current -## project. Use it when your project needs a package it doesn't explicitly import - -## including "main" packages. -# required = ["github.com/user/thing/cmd/thing"] - -## "ignored" lists a set of packages (not projects) that are ignored when -## dep statically analyzes source code. Ignored packages can be in this project, -## or in a dependency. -# ignored = ["github.com/user/project/badpkg"] - -## Constraints are rules for how directly imported projects -## may be incorporated into the depgraph. They are respected by -## dep whether coming from the Gopkg.toml of the current project or a dependency. -# [[constraint]] -## Required: the root import path of the project being constrained. -# name = "github.com/user/project" -# -## Recommended: the version constraint to enforce for the project. -## Only one of "branch", "version" or "revision" can be specified. -# version = "1.0.0" -# branch = "master" -# revision = "abc123" -# -## Optional: an alternate location (URL or import path) for the project's source. -# source = "https://github.com/myfork/package.git" -# -## "metadata" defines metadata about the dependency or override that could be used -## by other independent systems. The metadata defined here will be ignored by dep. -# [metadata] -# key1 = "value that convey data to other systems" -# system1-data = "value that is used by a system" -# system2-data = "value that is used by another system" - -## Overrides have the same structure as [[constraint]], but supersede all -## [[constraint]] declarations from all projects. Only [[override]] from -## the current project's are applied. -## -## Overrides are a sledgehammer. Use them only as a last resort. -# [[override]] -## Required: the root import path of the project being constrained. -# name = "github.com/user/project" -# -## Optional: specifying a version constraint override will cause all other -## constraints on this project to be ignored; only the overridden constraint -## need be satisfied. -## Again, only one of "branch", "version" or "revision" can be specified. -# version = "1.0.0" -# branch = "master" -# revision = "abc123" -# -## Optional: specifying an alternate source location as an override will -## enforce that the alternate location is used for that project, regardless of -## what source location any dependent projects specify. -# source = "https://github.com/myfork/package.git" - - - -[[constraint]] - branch = "master" - name = "github.com/BurntSushi/toml" - -[[constraint]] - branch = "master" - name = "github.com/Joker/jade" - -[[constraint]] - branch = "master" - name = "github.com/Unknwon/i18n" - -[[constraint]] - branch = "master" - name = "github.com/aymerick/raymond" - -[[constraint]] - name = "github.com/davecgh/go-spew" - version = "1.1.0" - -[[constraint]] - branch = "master" - name = "github.com/eknkc/amber" - -[[constraint]] - branch = "master" - name = "github.com/esemplastic/unis" - -[[constraint]] - branch = "master" - name = "github.com/flosch/pongo2" - -[[constraint]] - name = "github.com/garyburd/redigo" - version = "1.1.0" - -[[constraint]] - branch = "master" - name = "github.com/gavv/monotime" - -[[constraint]] - name = "github.com/gorilla/websocket" - version = "1.1.0" - -[[constraint]] - name = "github.com/imdario/mergo" - version = "0.2.2" - -[[constraint]] - branch = "master" - name = "github.com/iris-contrib/httpexpect" - -[[constraint]] - branch = "master" - name = "github.com/klauspost/compress" - -[[constraint]] - branch = "master" - name = "github.com/microcosm-cc/bluemonday" - -[[constraint]] - branch = "master" - name = "github.com/monoculum/formam" - -[[constraint]] - branch = "master" - name = "github.com/moul/http2curl" - -[[constraint]] - name = "github.com/pmezard/go-difflib" - version = "1.0.0" - -[[constraint]] - branch = "master" - name = "github.com/russross/blackfriday" - -[[constraint]] - branch = "master" - name = "github.com/satori/go.uuid" - -[[constraint]] - branch = "master" - name = "github.com/valyala/bytebufferpool" - -[[constraint]] - branch = "master" - name = "github.com/xeipuuv/gojsonschema" - -[[constraint]] - branch = "master" - name = "github.com/yalp/jsonpath" - -[[constraint]] - name = "github.com/yudai/gojsondiff" - version = "1.0.0" - -[[constraint]] - branch = "master" - name = "golang.org/x/crypto" - -[[constraint]] - branch = "master" - name = "golang.org/x/text" - -[[constraint]] - branch = "v2" - name = "gopkg.in/yaml.v2" - -[[constraint]] - branch = "master" - name = "github.com/PuerkitoBio/goquery" \ No newline at end of file diff --git a/HISTORY.md b/HISTORY.md index da6a71d8..9e57c7db 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -7,7 +7,7 @@ ### Looking for previous versions? - https://github.com/kataras/iris#version + https://github.com/kataras/iris#-version ### Should I upgrade my Iris? @@ -19,6 +19,142 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene For further installation support, please click [here](http://support.iris-go.com/d/16-how-to-install-iris-web-framework). +# Mo, 10 July 2017 | v8.0.0 + +## 📈 One and a half years with Iris and You... + +Despite the deflamations, the clickbait articles, the removed posts of mine at reddit/r/golang, the unexpected and inadequate ban from the gophers slack room by @dlsniper alone the previous week without any reason or inform, Iris is still here and will be. + +- 7070 github stars +- 749 github forks +- 1m total views at its documentation +- ~800$ at donations (there're a lot for a golang open-source project, thanks to you) +- ~550 reported bugs fixed +- ~30 community feature requests have been implemented + +## 🔥 Reborn + +As you may have heard I have huge responsibilities on my new position at Dubai nowdays, therefore I don't have the needed time to work on this project anymore. + +After a month of negotiations and searching I succeed to find a decent software engineer to continue my work on the open source community. + +The leadership of this, open-source, repository was transfered to [hiveminded](https://github.com/hiveminded), the author of iris-based [get-ion/ion](https://github.com/get-ion/ion), he actually did an excellent job on the framework, he kept the code as minimal as possible and at the same time added more features, examples and middleware(s). + +These types of projects need heart and sacrifices to continue offer the best developer experience like a paid software, please do support him as you did with me! + +## 📰 Changelog + +> app. = `app := iris.New();` **app.** + +> ctx. = `func(ctx context.Context) {` **ctx.** `}` + +### Docker + +Docker and kubernetes integration showcase, see the [iris-contrib/cloud-native-go](https://github.com/iris-contrib/cloud-native-go) repository as an example. + +### Logger + +* Logger which was an `io.Writer` was replaced with the pluggable `logrus`. + * which you still attach an `io.Writer` with `app.Logger().Out = an io.Writer`. + * iris as always logs only critical errors, you can disable them with `app.Logger().Level = iris.NoLog` + * the request logger outputs the incoming requests as INFO level. + +### Sessions + +Remove `ctx.Session()` and `app.AttachSessionManager`, devs should import and use the `sessions` package as standalone, it's totally optional, devs can use any other session manager too. [Examples here](sessions#table-of-contents). + +### Websockets + +The `github.com/kataras/iris/websocket` package does not handle the endpoint and client side automatically anymore. Example code: + +```go +func setupWebsocket(app *iris.Application) { + // create our echo websocket server + ws := websocket.New(websocket.Config{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + }) + ws.OnConnection(handleConnection) + // serve the javascript built'n client-side library, + // see weboskcets.html script tags, this path is used. + app.Any("/iris-ws.js", func(ctx context.Context) { + ctx.Write(websocket.ClientSource) + }) + + // register the server on an endpoint. + // see the inline javascript code in the websockets.html, this endpoint is used to connect to the server. + app.Get("/echo", ws.Handler()) +} +``` + +> More examples [here](websocket#table-of-contents) + +### View + +Rename `app.AttachView(...)` to `app.RegisterView(...)`. + +Users can omit the import of `github.com/kataras/iris/view` and use the `github.com/kataras/iris` package to +refer to the view engines, i.e: `app.RegisterView(iris.HTML("./templates", ".html"))` is the same as `import "github.com/kataras/iris/view" [...] app.RegisterView(view.HTML("./templates" ,".html"))`. + +> Examples [here](_examples/#view) + +### Security + +At previous versions, when you called `ctx.Remoteaddr()` Iris could parse and return the client's IP from the "X-Real-IP", "X-Forwarded-For" headers. This was a security leak as you can imagine, because the user can modify them. So we've disabled these headers by-default and add an option to add/remove request headers that are responsible to parse and return the client's real IP. + +```go +// WithRemoteAddrHeader enables or adds a new or existing request header name +// that can be used to validate the client's real IP. +// +// Existing values are: +// "X-Real-Ip": false, +// "X-Forwarded-For": false, +// "CF-Connecting-IP": false +// +// Look `context.RemoteAddr()` for more. +WithRemoteAddrHeader(headerName string) Configurator // enables a header. +WithoutRemoteAddrHeader(headerName string) Configurator // disables a header. +``` +For example, if you want to enable the "CF-Connecting-IP" header (cloudflare) +you have to add the `WithRemoteAddrHeader` option to the `app.Run` function, at the end of your program. + +```go +app.Run(iris.Addr(":8080"), iris.WithRemoteAddrHeader("CF-Connecting-IP")) +// This header name will be checked when ctx.RemoteAddr() called and if exists +// it will return the client's IP, otherwise it will return the default *http.Request's `RemoteAddr` field. +``` + +### Miscellaneous + +Fix [typescript tools](typescript). + +[_examples](_examples/) folder has been ordered by feature and usage: + - contains tests on some examples + - new examples added, one of them shows how the `reuseport` feature on UNIX and BSD systems can be used to listen for incoming connections, [see here](_examples/#http-listening) + + +Replace supervisor's tasks with events, like `RegisterOnShutdown`, `RegisterOnError`, `RegisterOnServe` and fix the (unharmful) race condition when output the banner to the console. Global notifier for interrupt signals which can be disabled via `app.Run([...], iris.WithoutInterruptHandler)`, look [graceful-shutdown](_examples/http-listening/graceful-shutdown/main.go) example for more. + + +More handlers are ported to Iris (they can be used as they are without `iris.FromStd`), these handlers can be found at [iris-contrib/middleware](https://github.com/iris-contrib/middleware). Feel free to put your own there. + + +| Middleware | Description | Example | +| -----------|--------|-------------| +| [jwt](https://github.com/iris-contrib/middleware/tree/master/jwt) | Middleware checks for a JWT on the `Authorization` header on incoming requests and decodes it. | [iris-contrib/middleware/jwt/_example](https://github.com/iris-contrib/middleware/tree/master/jwt/_example) | +| [cors](https://github.com/iris-contrib/middleware/tree/master/cors) | HTTP Access Control. | [iris-contrib/middleware/cors/_example](https://github.com/iris-contrib/middleware/tree/master/cors/_example) | +| [secure](https://github.com/iris-contrib/middleware/tree/master/secure) | Middleware that implements a few quick security wins. | [iris-contrib/middleware/secure/_example](https://github.com/iris-contrib/middleware/tree/master/secure/_example/main.go) | +| [tollbooth](https://github.com/iris-contrib/middleware/tree/master/tollboothic) | Generic middleware to rate-limit HTTP requests. | [iris-contrib/middleware/tollbooth/_examples/limit-handler](https://github.com/iris-contrib/middleware/tree/master/tollbooth/_examples/limit-handler) | +| [cloudwatch](https://github.com/iris-contrib/middleware/tree/master/cloudwatch) | AWS cloudwatch metrics middleware. |[iris-contrib/middleware/cloudwatch/_example](https://github.com/iris-contrib/middleware/tree/master/cloudwatch/_example) | +| [new relic](https://github.com/iris-contrib/middleware/tree/master/newrelic) | Official [New Relic Go Agent](https://github.com/newrelic/go-agent). | [iris-contrib/middleware/newrelic/_example](https://github.com/iris-contrib/middleware/tree/master/newrelic/_example) | +| [prometheus](https://github.com/iris-contrib/middleware/tree/master/prometheus)| Easily create metrics endpoint for the [prometheus](http://prometheus.io) instrumentation tool | [iris-contrib/middleware/prometheus/_example](https://github.com/iris-contrib/middleware/tree/master/prometheus/_example) | + + +v7.x is deprecated because it sold as it is and it is not part of the public, stable `gopkg.in` iris versions. Developers/users of this library should upgrade their apps to v8.x, the refactor process will cost nothing for most of you, as the most common API remains as it was. The changelog history from that are being presented below. + + +# Th, 15 June 2017 | v7.2.0 + ### About our new home page http://iris-go.com @@ -29,9 +165,6 @@ Thanks to [Santosh Anand](https://github.com/santoshanand) the http://iris-go.co The amount of the next two or three donations you'll send they will be immediately transferred to his own account balance, so be generous please! - -# Th, 15 June 2017 | v7.2.0 - ### Cache Declare the `iris.Cache alias` to the new, improved and most-suited for common usage, `cache.Handler function`. @@ -52,20 +185,20 @@ Declare the `iris.Cache alias` to the new, improved and most-suited for common u - **Fix** `app.StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string)`. Examples: -- [Embedding Files Into Executable App](_examples/beginner/file-server/embedding-files-into-app) -- [Single Page Application](_examples/beginner/file-server/single-page-application) -- [Embedding Single Page Application](_examples/beginner/file-server/embedding-single-page-application) +- [Embedding Files Into Executable App](_examples/file-server/embedding-files-into-app) +- [Single Page Application](_examples/file-server/single-page-application) +- [Embedding Single Page Application](_examples/file-server/embedding-single-page-application) -> [app.StaticWeb](_examples/beginner/file-server/basic/main.go) doesn't works for root request path "/" anymore, use the new `app.SPA` instead. +> [app.StaticWeb](_examples/file-server/basic/main.go) doesn't works for root request path "/" anymore, use the new `app.SPA` instead. ### WWW subdomain entry -- [Example](_examples/intermediate/subdomains/www/main.go) added to copy all application's routes, including parties, to the `www.mydomain.com` +- [Example](_examples/subdomains/www/main.go) added to copy all application's routes, including parties, to the `www.mydomain.com` ### Wrapping the Router -- [Example](_examples/beginner/routing/custom-wrapper/main.go) added to show you how you can use the `app.WrapRouter` +- [Example](_examples/routing/custom-wrapper/main.go) added to show you how you can use the `app.WrapRouter` to implement a similar to `app.SPA` functionality, don't panic, it's easier than it sounds. diff --git a/LICENSE b/LICENSE index 05050318..2d4d4a32 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. +Copyright (c) 2017 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 @@ -10,38 +10,7 @@ notice, this list of conditions and the following disclaimer. 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 Gerasimos Maropoulos nor the name of his -username, kataras, 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. - - -The Go Programming Language is redistributed with the following disclaimer: - -Copyright (c) 2009 The Go 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 Google Inc. nor the names of its + * 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. diff --git a/README.md b/README.md index 9c26193d..7c31938c 100644 --- a/README.md +++ b/README.md @@ -1,113 +1,73 @@ -# 02 July 2017 - -### DEPRECATED - -Iris has been acquired so development is up to the community, there are two active iris-based communities so far. - -Use one of these projects instead: - -https://github.com/get-ion/ion - -**Ion is a fast, simple and efficient micro web framework for Go. It provides a beautifully expressive and easy to use foundation for your next website, API, or distributed app.** - -- a bit faster than Iris version 7, based on `ab` -- stable api -- more examples -- sessions, websockets, typescript and cloud editor(fixed) on different packages -- test cov, including examples -- slack bot for support automation -- has a FAQ page which is part of the gitbook.com beta program -- central issue portal -- HuHu supported - -https://github.com/go-siris/siris - -**A fast, cross-platform and efficient web framework with robust set of well-designed features, written entirely in Go.** - -- three maintainers -- plan to stabilize api, no unneeded changes -- plan to increase test-coverage -- plan to add more middlewares and examples - - -> If your team's project is missing from this list, please contact with me. - -# IRIS HAS BEEN ACQUIRED - -Iris project has been acquired by a Dubai-based startup. - -Both sides agree that every related public data should remain open for at least 30 days. - -After the period of 30 days, company has the proprietary rights to delete or transfer this repository and all its related public data forever without any warnings. - -The company may or may not reveal its true identity to the public. - -Transaction of the public domains still in-progress: - -- http://iris-go.com -- https://kataras.rocket.chat/channel/iris - -View-accessed users can clone the current state of the project's public repositories and use without any warranties. - -From now on, Original Author owns a high position to the company's table. - -At any circumstances, - -Original Author keeps the creation rights. - -# About the future of Iris - -Clone the repository today because if I can't find a new lead maintainer for the [v7.2](https://github.com/kataras/iris-v7-29d) you, as community, will have to find a way to communicate about its future, the name "iris go" was taken by the company too, so it will be nice if the future main contributor change its name too, if you don't do it I will not beat you but I don't know the full company's law-plan for this, yet. - -All donators, without any exception, will have my support for at least 6 months (for all iris versions), we have a private room at the [chat](https://kataras.rocket.chat/channel/iris). - -Don't worry **I will not let you down**, we're trying to find a decent open-source contributor to continue the Iris' open-source codebase. I'm already in touch with some good gophers but **If you're willing to maintain this project** please [send](#contact) me details about your experience, general bio and your github username. - -**I am really thankful for all of your support to me and the community, all donations, all bug reports, all comments without any exception. I did proceeded with all my physical abilities so far but unfortunately there weren't enough for my survivor. I'm really sorry if the latest news made iris open-source community disappointed but you have to see things from my point view, I was one step before bankruptcy, I had no other choice but accept the offer.** - # ![Logo created by @santoshanand](logo_white_35_24.png) Iris -A fast, cross-platform and efficient web framework with robust set of well-designed features, written entirely in Go. +Iris is a fast, simple and efficient micro web framework for Go. It provides a beautifully expressive and easy to use foundation for your next website, API, or distributed app. + +[![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris) +[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) +[![godocs](https://img.shields.io/badge/godocs-8.x.x-0366d6.svg?style=flat-square)](https://godoc.org/github.com/kataras/iris) +[![get support](https://img.shields.io/badge/get-support-cccc00.svg?style=flat-square)](http://support.iris-go.com) +[![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples) +[![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) -[![Build status](https://api.travis-ci.org/kataras/iris.svg?branch=master&style=flat-square)](https://travis-ci.org/kataras/iris) -[![Report card](https://img.shields.io/badge/report%20card%20-a%2B-F44336.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) -[![Support forum](https://img.shields.io/badge/support-page-ec2eb4.svg?style=flat-square)](http://support.iris-go.com) -[![Examples](https://img.shields.io/badge/howto-examples-3362c2.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples#table-of-contents) -[![Godocs](https://img.shields.io/badge/7.2.0-%20documentation-5272B4.svg?style=flat-square)](https://godoc.org/github.com/kataras/iris) -[![Chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) -[![Buy me a cup of coffee](https://img.shields.io/badge/support-%20open--source-F4A460.svg?logo=data:image%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxwYXRoIGZpbGw9InJnYigyMjAsMjIwLDIyMCkiIGQ9Ik04ODYuNiwzMDUuM2MtNDUuNywyMDMuMS0xODcsMzEwLjMtNDA5LjYsMzEwLjNoLTc0LjFsLTUxLjUsMzI2LjloLTYybC0zLjIsMjEuMWMtMi4xLDE0LDguNiwyNi40LDIyLjYsMjYuNGgxNTguNWMxOC44LDAsMzQuNy0xMy42LDM3LjctMzIuMmwxLjUtOGwyOS45LTE4OS4zbDEuOS0xMC4zYzIuOS0xOC42LDE4LjktMzIuMiwzNy43LTMyLjJoMjMuNWMxNTMuNSwwLDI3My43LTYyLjQsMzA4LjktMjQyLjdDOTIxLjYsNDA2LjgsOTE2LjcsMzQ4LjYsODg2LjYsMzA1LjN6Ii8%2BPHBhdGggZmlsbD0icmdiKDIyMCwyMjAsMjIwKSIgZD0iTTc5MS45LDgzLjlDNzQ2LjUsMzIuMiw2NjQuNCwxMCw1NTkuNSwxMEgyNTVjLTIxLjQsMC0zOS44LDE1LjUtNDMuMSwzNi44TDg1LDg1MWMtMi41LDE1LjksOS44LDMwLjIsMjUuOCwzMC4ySDI5OWw0Ny4zLTI5OS42bC0xLjUsOS40YzMuMi0yMS4zLDIxLjQtMzYuOCw0Mi45LTM2LjhINDc3YzE3NS41LDAsMzEzLTcxLjIsMzUzLjItMjc3LjVjMS4yLTYuMSwyLjMtMTIuMSwzLjEtMTcuOEM4NDUuMSwxODIuOCw4MzMuMiwxMzAuOCw3OTEuOSw4My45TDc5MS45LDgzLjl6Ii8%2BPC9zdmc%2B)](https://github.com/kataras/iris#buy-me-a-cup-of-coffee) -

-This benchmark measures results from 'real-world' instead of 'hello-world' application source code. | Last Update At: July 21, 2016. | Shows: Processing Time Horizontal Graph. | Who did:  Third-party source. Transparent achievement. +Third-party source for transparency.

-Build your own web applications and portable APIs with the highest performance and countless potentials. +# Mo, 10 July 2017 | v8.0.0 -If you're coming from [Node.js](https://nodejs.org) world, this is the [expressjs](https://github.com/expressjs/express)++ equivalent for the [Go Programming Language](https://golang.org). +### 📈 One and a half years with Iris and You... +- 7070 github stars +- 749 github forks +- 1m total views at its documentation +- ~800$ at donations (there're a lot for a golang open-source project, thanks to you) +- ~550 reported bugs fixed +- ~30 community feature requests have been implemented -Table of contents ------------ +### 🔥 Reborn -* [Installation](#installation) -* [Feature overview](#feature-overview) -* [Documentation](#documentation) - * [Examples](https://github.com/kataras/iris/tree/master/_examples) - * [Reload on source code changes](#reload-on-source-code-changes) -* [Support](#support) - * [Buy me a cup of coffee?](#buy-me-a-cup-of-coffee) -* [Third-party middleware list](#third-party-middleware) -* [Testing](#testing) -* [Philosophy](#philosophy) -* [People](#people) - * [Legends](#legends) - * [Contact](#contact) -* [Versioning](#version) +As you may have heard I have huge responsibilities on my new position at Dubai nowdays, therefore I don't have the needed time to work on this project anymore. + +After almost a month of negotiations and searching I succeed to find a decent software engineer to continue my work on the open source community. + +The leadership of this, open-source, repository was transfered to [hiveminded](https://github.com/hiveminded). + +These types of projects need heart and sacrifices to continue offer the best developer experience like a paid software, please do support him as you did with me! + +> Please [contact](https://kataras.rocket.chat/channel/iris) with the project team if you want to help at the development process! + +### 📑 Table of contents + +* [Installation](#-installation) +* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#mo-10-july-2017--v800) +* [Learn](#-learn) + * [HTTP Listening](_examples/#http-listening) + * [Configuration](_examples/#configuration) + * [Routing, Grouping, Dynamic Path Parameters, "Macros" and Custom Context](_examples/#routing-grouping-dynamic-path-parameters-macros-and-custom-context) + * [Subdomains](_examples/#subdomains) + * [Wrap `http.Handler/HandlerFunc`](_examples/#convert-httphandlerhandlerfunc) + * [View](_examples/#view) + * [Authentication](_examples/#authentication) + * [File Server](_examples/#file-server) + * [How to Read from `context.Request() *http.Request`](_examples/#how-to-read-from-contextrequest-httprequest) + * [How to Write to `context.ResponseWriter() http.ResponseWriter`](_examples/#how-to-write-to-contextresponsewriter-httpresponsewriter) + * [Test](_examples/#testing) + * [Cache](cache/#table-of-contents) + * [Sessions](sessions/#table-of-contents) + * [Websockets](websocket/#table-of-contents) + * [Miscellaneous](_examples/#miscellaneous) + * [Typescript Automation Tools](typescript/#table-of-contents) + * [Tutorial: Online Visitors](_examples/tutorial/online-visitors) + * [Tutorial: URL Shortener using BoltDB](_examples/tutorial/url-shortener) +* [Middleware](middleware/) +* [Philosophy](#-philosophy) +* [Support](#-support) +* [Versioning](#-version) * [When should I upgrade?](#should-i-upgrade-my-iris) * [Where can I find older versions?](#where-can-i-find-older-versions) +* [People](#-people) -Installation ------------ +### 🚀 Installation The only requirement is the [Go Programming Language](https://golang.org/dl/), at least version 1.8 @@ -115,393 +75,154 @@ The only requirement is the [Go Programming Language](https://golang.org/dl/), a $ go get -u github.com/kataras/iris ``` -> Iris uses the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature, so you get truly reproducible builds, as this method guards against upstream renames and deletes. -For further installation support, please navigate [here](http://support.iris-go.com/d/16-how-to-install-iris-web-framework). +> _iris_ takes advantage of the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature. You get truly reproducible builds, as this method guards against upstream renames and deletes. ```go +// file: main.go package main - import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/view" ) - -// 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() - - // 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.AttachView(view.HTML("./views", ".html").Reload(true)) - - // Regster custom handler for specific http errors. - app.OnErrorCode(iris.StatusInternalServerError, func(ctx context.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 context.Context) { - ctx.Application().Log("Begin request for path: %s", ctx.Path()) - ctx.Next() - }) - - // app.Done(func(ctx context.Context) {}) - - // Method POST: http://localhost:8080/decode - app.Post("/decode", func(ctx context.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 context.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) - } - - // Listen for incoming HTTP/1.x & HTTP/2 clients on localhost port 8080. - app.Run(iris.Addr(":8080"), iris.WithCharset("UTF-8")) -} + // Load all templates from the "./templates" folder + // where extension is ".html" and parse them + // using the standard `html/template` package. + app.RegisterView(iris.HTML("./templates", ".html")) -func logThisMiddleware(ctx context.Context) { - ctx.Application().Log("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() -} + // Method: GET + // Resource: http://localhost:8080 + app.Get("/", func(ctx context.Context) { + // Bind: {{.message}} with "Hello world!" + ctx.ViewData("message", "Hello world!") + // Render template file: ./templates/hello.html + ctx.View("hello.html") + }) -func profileByUsername(ctx context.Context) { - // .Params are used to get dynamic path parameters. - username := ctx.Params().Get("username") - ctx.ViewData("Username", username) - // renders "./views/users/profile.html" - // with {{ .Username }} equals to the username dynamic path parameter. - ctx.View("users/profile.html") + // Start the server using a network address and block. + app.Run(iris.Addr(":8080")) } +``` +```html + + + + Hello Page + + +

{{.message}}

+ + +``` -func getUserByID(ctx context.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) -} +```sh +$ go run main.go +> Now listening on: http://localhost:8080 +> Application started. Press CTRL+C to shut down. +``` -func createUser(ctx context.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/users/create_verification.html" - // with {{ . }} equals to the User object, i.e {{ .Username }} , {{ .Firstname}} etc... - ctx.ViewData("", user) - ctx.View("users/create_verification.html") +
+Hello World with Go 1.9 + +If you've installed Go 1.9 then you can omit the `github.com/kataras/iris/context` package from the imports statement. + +```go +// +build go1.9 + +package main + +import "github.com/kataras/iris" + +func main() { + app := iris.New() + app.RegisterView(iris.HTML("./templates", ".html")) + + app.Get("/", func(ctx iris.Context) { + ctx.ViewData("message", "Hello world!") + ctx.View("hello.html") + }) + + app.Run(iris.Addr(":8080")) } ``` -### Reload on source code changes +We expect Go version 1.9 to be released in August, however you can install Go 1.9 beta today. +### Installing Go 1.9beta2 + +1. Go to https://golang.org/dl/#go1.9beta2 +2. Download a compatible, with your OS, archieve, i.e `go1.9beta2.windows-amd64.zip` +3. Unzip the contents of `go1.9beta2.windows-amd64.zip/go` folder to your $GOROOT, i.e `C:\Go` +4. Open a terminal and execute `go version`, it should output the go1.9beta2 version, i.e: ```sh -$ go get -u github.com/kataras/rizla -$ cd $GOPATH/src/mywebapp -$ rizla main.go +C:\Users\hiveminded>go version +go version go1.9beta2 windows/amd64 ``` -> Psst: Wanna go to [_examples](https://github.com/kataras/iris/tree/master/_examples) to see more code-snippets? +
-Feature Overview ------------ +
+Why another new web framework? -- Focus on high performance -- Build RESTful APIs with our expressionist path syntax, i.e `{userid:int min(1)}`, `{asset:path}`, `{custom regexp([a-z]+)}` -- Automatically install and serve certificates from https://letsencrypt.org -- Robust routing and middleware ecosystem -- Request-Scoped Transactions -- Group API's and subdomains with wildcard support -- Body binding for JSON, XML, Forms, can be extended to use your own custom binders -- More than 50 handy functions to send HTTP responses -- View system: supporting more than 6+ template engines, with prerenders. You can still use your favorite -- Define virtual hosts and (wildcard) subdomains with path level routing -- Graceful shutdown -- Limit request body -- Localization i18N -- Serve static and embedded files -- Cache -- Log requests -- Customizable format and output for the logger -- Customizable HTTP errors -- Compression (Gzip) -- Authentication - - OAuth, OAuth2 supporting 27+ popular websites - - JWT - - Basic Authentication -- HTTP Sessions -- Add / Remove trailing slash from the URL with option to redirect -- Redirect requests - - HTTP to HTTPS - - HTTP to HTTPS WWW - - HTTP to HTTPS non WWW - - Non WWW to WWW - - WWW to non WWW -- Highly scalable rich content render (Markdown, JSON, JSONP, XML...) -- Websocket-only API similar to socket.io -- Hot Reload on source code changes -- Typescript integration + Web IDE -- Checks for updates at startup -- Highly customizable -- Feels like you used iris forever, thanks to its Fluent API -- And many others... +_iris_ is easy, it has a familiar API while in the same has far more features than [Gin](https://github.com/gin-gonic/gin) or [Martini](https://github.com/go-martini/martini). -Documentation ------------ +You own your code —it will never generate (unfamiliar) code for you, like [Beego](https://github.com/astaxie/beego), [Revel](https://github.com/revel/revel) and [Buffalo](https://github.com/gobuffalo/buffalo) do. - +It's not just-another-router but its overall performance is equivalent with something like [httprouter](https://github.com/julienschmidt/httprouter). -Small but practical [examples](https://github.com/kataras/iris/tree/master/_examples#table-of-contents) --they cover each feature. +Unlike [fasthttp](https://github.com/valyala/fasthttp), iris provides full HTTP/2 support for free. -Wanna create your own fast URL Shortener Service Using Iris? --click [here](https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7) to learn how. +Compared to the rest open source projects, this one is very active and you get answers almost immediately. -[Godocs](https://godoc.org/github.com/kataras/iris) --for deep understanding. +
+### 👥 Community -Support ------------- +Join the welcoming community of fellow _iris_ developers in [rocket.chat](https://kataras.rocket.chat/channel/iris). -- [Post](http://support.iris-go.com) a feature request or report a bug, will help to make the framework even better. -- :star: and watch [the project](https://github.com/kataras/iris/stargazers), will notify you about updates. -- :earth_americas: publish [an article](https://medium.com/) or share a [tweet](https://twitter.com/) about Iris. -- Donations, will help me to continue. +### 📖 Learn -I'll be glad to talk with you about **your awesome feature requests**, -open a new [discussion](http://support.iris-go.com), you will be heard! +The awesome _iris_ community is always adding new examples, [_examples](_examples/) is a great place to get started! -Thanks in advance! +Read the [godocs](https://godoc.org/github.com/kataras/iris) for a better understanding. -Buy me a cup of coffee? ------------- +### 🤔 Philosophy -Iris is free and open source but developing it has taken thousands of hours of my time and a large part of my sanity. If you feel this web framework useful to you, it would go a great way to ensuring that I can afford to take the time to continue to develop it. +The _iris_ philosophy is to provide robust tooling for HTTP, making it a great solution for single page applications, web sites, hybrids, or public HTTP APIs. Keep note that, today, iris is faster than apache+nginx itself. +_iris_ does not force you to use any specific ORM. With support for the most popular template engines, websocket server and a fast sessions manager you can quickly craft your perfect application. -I spend all my time in the construction of Iris, therefore I have no income value. +### 💙 Support -Feel free to send **any** amount through paypal: +- [Post](http://support.iris-go.com) a feature request or report a bug +- :star: and watch the public [repository](https://github.com/kataras/iris/stargazers), will keep you up to date +- :earth_americas: publish [an article](https://medium.com/search?q=iris) or share a [tweet](https://twitter.com/hashtag/golang) about your personal experience with iris -[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=kataras2006%40hotmail%2ecom&lc=GR&item_name=Iris%20web%20framework&item_number=iriswebframeworkdonationid2016¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted) +### 📌 Version -> Please check your e-mail after your donation. - -Thanks for your gratitude and finance help ♡ - - - - -Third Party Middleware ------------- - -Iris has its own middleware form of `func(ctx context.Context)` but it's also compatible with all `net/http` middleware forms. See [here](https://github.com/kataras/iris/tree/master/_examples/beginner/convert-handlers). - -I'm sure that each of you have, already, found his own favorite list but here's a small list of third-party handlers: - -| Middleware | Author | Description | -| -----------|--------|-------------| -| [tollbooth](https://github.com/didip/tollbooth) | [Didip Kerabat](https://github.com/didip) | Generic middleware to rate-limit HTTP requests. [Example](https://github.com/didip/tollbooth/pull/34) | -| [goth](https://github.com/markbates/goth) | [Mark Bates](https://github.com/markbates) | OAuth, OAuth2 authentication. [Example](https://github.com/kataras/iris/tree/master/_examples/intermediate/oauth2) | -| [binding](https://github.com/mholt/binding) | [Matt Holt](https://github.com/mholt) | Data binding from HTTP requests into structs | -| [cloudwatch](https://github.com/cvillecsteele/negroni-cloudwatch) | [Colin Steele](https://github.com/cvillecsteele) | AWS cloudwatch metrics middleware | -| [csp](https://github.com/awakenetworks/csp) | [Awake Networks](https://github.com/awakenetworks) | [Content Security Policy](https://www.w3.org/TR/CSP2/) (CSP) support | -| [delay](https://github.com/jeffbmartinez/delay) | [Jeff Martinez](https://github.com/jeffbmartinez) | Add delays/latency to endpoints. Useful when testing effects of high latency | -| [New Relic Go Agent](https://github.com/yadvendar/negroni-newrelic-go-agent) | [Yadvendar Champawat](https://github.com/yadvendar) | Official [New Relic Go Agent](https://github.com/newrelic/go-agent) (currently in beta) | -| [gorelic](https://github.com/jingweno/negroni-gorelic) | [Jingwen Owen Ou](https://github.com/jingweno) | New Relic agent for Go runtime | -| [JWT](https://github.com/iris-contrib/middleware/tree/master/jwt) | [Auth0](https://github.com/auth0) | Middleware checks for a JWT on the `Authorization` header on incoming requests and decodes it. [Example](https://github.com/iris-contrib/middleware/tree/master/jwt/_example)| -| [logrus](https://github.com/meatballhat/negroni-logrus) | [Dan Buch](https://github.com/meatballhat) | Logrus-based logger | -| [onthefly](https://github.com/xyproto/onthefly) | [Alexander Rødseth](https://github.com/xyproto) | Generate TinySVG, HTML and CSS on the fly | -| [permissions2](https://github.com/xyproto/permissions2) | [Alexander Rødseth](https://github.com/xyproto) | Cookies, users and permissions | -| [prometheus](https://github.com/zbindenren/negroni-prometheus) | [Rene Zbinden](https://github.com/zbindenren) | Easily create metrics endpoint for the [prometheus](http://prometheus.io) instrumentation tool | -| [render](https://github.com/unrolled/render) | [Cory Jacobsen](https://github.com/unrolled) | Render JSON, XML and HTML templates | -| [RestGate](https://github.com/pjebs/restgate) | [Prasanga Siripala](https://github.com/pjebs) | Secure authentication for REST API endpoints | -| [secure](https://github.com/iris-contrib/middleware/tree/master/secure) | [Cory Jacobsen](https://github.com/unrolled) | Middleware that implements a few quick security wins | -| [stats](https://github.com/thoas/stats) | [Florent Messa](https://github.com/thoas) | Store information about your web application (response time, etc.) | -| [VanGoH](https://github.com/auroratechnologies/vangoh) | [Taylor Wrobel](https://github.com/twrobel3) | Configurable [AWS-Style](http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html) HMAC authentication middleware | -| [xrequestid](https://github.com/pilu/xrequestid) | [Andrea Franz](https://github.com/pilu) | Middleware that assigns a random X-Request-Id header to each request | -| [digits](https://github.com/bamarni/digits) | [Bilal Amarni](https://github.com/bamarni) | Middleware that handles [Twitter Digits](https://get.digits.com/) authentication | - -Feel free to put up a [PR](https://github.com/iris-contrib/middleware) your middleware! - -Testing ------------- - -The `httptest` package is your way for end-to-end HTTP testing, it uses the httpexpect library created by our friend, [gavv](https://github.com/gavv). - -A simple test is located to [./_examples/intermediate/httptest/main_test.go](https://github.com/kataras/iris/blob/master/_examples/intermediate/httptest/main_test.go) - -Philosophy ------------- - -The Iris philosophy is to provide robust tooling for HTTP, making it a great solution for single page applications, web sites, hybrids, or public HTTP APIs. Keep note that, today, iris is faster than apache+nginx itself. - -Iris does not force you to use any specific ORM or template engine. With support for the most popular template engines, you can quickly craft your perfect application. - - -People ------------- - -The author of Iris is [@kataras](https://github.com/kataras). - -However the real Success of Iris belongs to you with your bug reports and feature requests that made this Framework so Unique. - - -### Legends - -I really need to thank each one of them because they stood up[♡](https://github.com/kataras/iris#support) to keep this project alive and active. - -[Juan Sebastián Suárez Valencia](https://github.com/Juanses) donated 20 EUR at September 11 of 2016 - -[Bob Lee](https://github.com/li3p) donated 20 EUR at September 16 of 2016 - -[Celso Luiz](https://github.com/celsosz) donated 50 EUR at September 29 of 2016 - -[Ankur Srivastava](https://github.com/ansrivas) donated 20 EUR at October 2 of 2016 - -[Damon Zhao](https://github.com/se77en) donated 20 EUR at October 21 of 2016 - -[exponity - consulting & digital transformation](https://github.com/exponity) donated 30 EUR at November 4 of 2016 - -[Thomas Fritz](https://github.com/thomasfr) donated 25 EUR at Jenuary 8 of 2017 - -[Thanos V.](http://mykonosbiennale.com/) donated 20 EUR at Jenuary 16 of 2017 - -[George Opritescu](https://github.com/International) donated 20 EUR at February 7 of 2017 - -[Lex Tang](https://github.com/lexrus) donated 20 EUR at February 22 of 2017 - -[Conrad Steenberg](https://github.com/hengestone) donated 25 EUR at March 23 of 2017 - -

- - - - - - - - - -
- - -What people say - - - -What people say - - -
- - - - - - - - - -

- -### Contact - -Besides the fact that we have a [community chat][Chat] for questions or reports and ideas, [stackoverflow](http://stackoverflow.com/) section for generic go+iris questions and the [iris support](http://support.iris-go.com) for bug reports and feature requests, you can also contact with me, as a person who is always open to help you: - -- [Twitter](https://twitter.com/MakisMaropoulos) -- [Facebook](https://facebook.com/kataras.gopher) -- [Linkedin](https://www.linkedin.com/in/gerasimos-maropoulos) - -Version ------------- - -Current: **7.2.0** +Current: **8.0.0** Each new release is pushed to the master. It stays there until the next version. When a next version is released then the previous version goes to its own branch with `gopkg.in` as its import path (and its own vendor folder), in order to keep it working "for-ever". -Community members can request additional features or report a bug fix for a specific iris version. +Changelog of the current version can be found at the [HISTORY](HISTORY.md) file. +#### Should I upgrade my iris? -### Should I upgrade my Iris? +Developers are not forced to use the latest _iris_ version, they can use any version in production, they can update at any time they want. -Developers are not forced to use the latest Iris version, they can use any version in production, they can update at any time they want. +Testers should upgrade immediately, if you're willing to use _iris_ in production you can wait a little more longer, transaction should be as safe as possible. -Testers should upgrade immediately, if you're willing to use Iris in production you can wait a little more longer, transaction should be as safe as possible. - -### Where can I find older versions? - -Each Iris version is independent. Only bug fixes, Router's API and experience are kept. +#### Where can I find older versions? Previous versions can be found at [releases page](https://github.com/kataras/iris/releases). -License ------------- +### 🥇 People -Unless otherwise noted, the source files are distributed -under the BSD-3-Clause License found in the [LICENSE file](LICENSE). +The original author of _iris_ is [Gerasimos Maropoulos](https://github.com/kataras) -Note that some third-party packages that you use with Iris may requires -different license agreements. +The current lead maintainer is [Bill Qeras, Jr.](https://github.com/hiveminded) -[Chat]: https://kataras.rocket.chat/channel/iris +[List of all contributors](https://github.com/kataras/iris/graphs/contributors) + +[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=kataras2006%40hotmail%2ecom&lc=GR&item_name=Iris%20web%20framework&item_number=iriswebframeworkdonationid2016¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted) \ No newline at end of file diff --git a/_examples/README.md b/_examples/README.md index 3d635972..b9ab6a34 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -1,110 +1,164 @@ # Examples -This folder provides easy to understand code snippets on how to get started with web development with the Go programming language using the [Iris](https://github.com/kataras/iris) web framework. +Please do learn how [net/http](https://golang.org/pkg/net/http/) std package works, first. -It doesn't contains "best ways" neither explains all its features. It's just a simple, practical cookbook for young Gophers! +This folder provides easy to understand code snippets on how to get started with [iris](https://github.com/kataras/iris) micro web framework. -## Table of contents +It doesn't always contain the "best ways" but it does cover each important feature that will make you so excited to GO with iris! -* [Level: Beginner](beginner) - * [Hello world](beginner/hello-world/main.go) - * [Overview](beginner/overview/main.go) - * [Listening](beginner/listening) - * [Common, with address](beginner/listening/listen-addr/main.go) - * [UNIX socket file](beginner/listening/listen-unix/main.go) - * [TLS](beginner/listening/listen-tls/main.go) - * [Letsencrypt (Automatic Certifications)](beginner/listening/listen-letsencrypt/main.go) - * [Custom TCP Listener](beginner/listening/custom-listener/main.go) - * [Configuration](beginner/configuration) - * [Basic way](beginner/configuration/basic/main.go) - * [Functional way](beginner/configuration/functional/main.go) - * [Import from YAML file](beginner/configuration/from-yaml-file/main.go) - * [Import from TOML file](beginner/configuration/from-toml-file/main.go) - * [Routing](beginner/routing) - * [Overview](beginner/routing/overview/main.go) - * [Basic](beginner/routing/basic/main.go) - * [Dynamic Path](beginner/routing/dynamic-path/main.go) - * [Reverse routing](beginner/routing/reverse/main.go) - * [Custom wrapper](beginner/routing/custom-wrapper/main.go) - * [Transform any third-party handler to iris-compatible handler](beginner/convert-handlers) - * [From func(http.ResponseWriter, *http.Request, http.HandlerFunc)](beginner/convert-handlers/negroni-like/main.go) - * [From http.Handler or http.HandlerFunc](beginner/convert-handlers/nethttp/main.go) - * [Internal Application File Logger](beginner/file-logger/main.go) - * [Custom HTTP Errors](beginner/http-errors/main.go) - * [Write JSON](beginner/write-json/main.go) - * [Read JSON](beginner/read-json/main.go) - * [Read Form](beginner/read-form/main.go) - * [Favicon](beginner/favicon/main.go) - * [File Server](beginner/file-server) - * [Basic](beginner/file-server/basic/main.go) - * [Embedding Files Into App Executable File](beginner/file-server/embedding-files-into-app/main.go) - * [Single Page Application](beginner/file-server/single-page-application/main.go) - * [Embedding Single Page Application](beginner/file-server/embedding-single-page-application/main.go) - * [Send Files](beginner/send-files/main.go) - * [Stream Writer](beginner/stream-writer/main.go) - * [Send An E-mail](beginner/e-mail/main.go) - * [Upload/Read Files](beginner/upload-files/main.go) - * [Recovery](beginner/recover/main.go) - * [Profiling (pprof)](beginner/pprof/main.go) - * [Request Logger](beginner/request-logger/main.go) - * [Basic Authentication](beginner/basicauth/main.go) -* [Level: Intermediate](intermediate) - * [JWT](https://github.com/iris-contrib/middleware/blob/master/jwt/_example/main.go) - * [OAUth2](intermediate/oauth2/main.go) - * [CORS](https://github.com/iris-contrib/middleware/blob/master/cors/_example/main.go) - * [Transactions](intermediate/transactions/main.go) - * [HTTP Testing](intermediate/httptest/main_test.go) - * [Watch & Compile Typescript source files](intermediate/typescript/main.go) - * [Cloud Editor](intermediate/cloud-editor/main.go) - * [HTTP Access Control](intermediate/cors/main.go) - * [Cache Markdown](intermediate/cache-markdown/main.go) - * [Localization and Internationalization](intermediate/i18n/main.go) - * [Graceful Shutdown](intermediate/graceful-shutdown) - * [Basic and simple](intermediate/graceful-shutdown/basic/main.go) - * [Custom Host](intermediate/graceful-shutdown/custom-host/main.go) - * [Custom notifier](intermediate/graceful-shutdown/custom-notifier/main.go) - * [Custom HTTP Server](intermediate/custom-httpserver) - * [Iris way](intermediate/custom-httpserver/iris-way/main.go) - * [Standar way](intermediate/custom-httpserver/std-way/main.go) - * [More than one server](intermediate/custom-httpserver/multi/main.go) - * [Custom Context](intermediate/custom-context) - * [Method Overriding](intermediate/custom-context/method-overriding/main.go) - * [Route State](intermediate/route-state/main.go) - * [View Engine](intermediate/view) - * [Overview](intermediate/view/overview/main.go) - * [Hi](intermediate/view/template_html_0/main.go) - * [Showcase one simple Layout](intermediate/view/template_html_1/main.go) - * [Layouts `yield` and `render` tmpl funcs](intermediate/view/template_html_2/main.go) - * [Showcase of the `urlpath` tmpl func](intermediate/view/template_html_3/main.go) - * [Showcase of the `url` tmpl func](intermediate/view/template_html_4/main.go) - * [Inject Data Between Handlers](intermediate/view/context-view-data/main.go) - * [Embedding Templates Into App Executable File](intermediate/view/embedding-templates-into-app/main.go) - * [Sessions](intermediate/sessions) - * [Overview](intermediate/sessions/overview/main.go) - * [Encoding & Decoding the Session ID: Secure Cookie](intermediate/sessions/securecookie/main.go) - * [Standalone](intermediate/sessions/standalone/main.go) - * [Flash Messages](intermediate/sessions/flash-messages/main.go) - * [With A Back-End Database](intermediate/sessions/database/main.go) - * [Password Hashing](intermediate/sessions/password-hashing/main.go) - * [Websockets](intermediate/websockets) - * [Ridiculous Simple](intermediate/websockets/ridiculous-simple/main.go) - * [Overview](intermediate/websockets/overview/main.go) - * [Connection List](intermediate/websockets/connectionlist/main.go) - * [Native Messages](intermediate/websockets/native-messages/main.go) - * [Secure](intermediate/websockets/secure/main.go) - * [Custom Go Client](intermediate/websockets/custom-go-client/main.go) - * [Subdomains](intermediate/subdomains) - * [Single](intermediate/subdomains/single/main.go) - * [Multi](intermediate/subdomains/multi/main.go) - * [Wildcard](intermediate/subdomains/wildcard/main.go) - * [WWW](intermediate/subdomains/www/main.go) -* [Level: Advanced](advanced) - * [Online Visitors](advanced/online-visitors/main.go) - * [URL Shortener using BoltDB](advanced/url-shortener/main.go) +### Overview + +- [Hello world!](hello-world/main.go) +- [Glimpse](overview/main.go) +- [Tutorial: Online Visitors](tutorial/online-visitors/main.go) +- [Tutorial: URL Shortener using BoltDB](tutorial/url-shortener/main.go) + +### HTTP Listening + +- [Common, with address](http-listening/listen-addr/main.go) +- [UNIX socket file](http-listening/listen-unix/main.go) +- [TLS](http-listening/listen-tls/main.go) +- [Letsencrypt (Automatic Certifications)](http-listening/listen-letsencrypt/main.go) +- Custom TCP Listener + * [common net.Listener](http-listening/custom-listener/main.go) + * [SO_REUSEPORT for unix systems](http-listening/custom-listener/unix-reuseport/main.go) +- Custom HTTP Server + * [iris way](http-listening/custom-httpserver/iris-way/main.go) + * [std way](http-listening/custom-httpserver/std-way/main.go) + * [multi server instances](http-listening/custom-httpserver/multi/main.go) +- Graceful Shutdown + * [using the `RegisterOnInterrupt`](http-listening/graceful-shutdown/default-notifier/main.go) + * [using a custom notifier](http-listening/graceful-shutdown/custom-notifier/main.go) + +### Configuration + +- [Functional](configuration/functional/main.go) +- [From Configuration Struct](configuration/from-configuration-struct/main.go) +- [Import from YAML file](configuration/from-yaml-file/main.go) +- [Import from TOML file](configuration/from-toml-file/main.go) -You may want to check out examples for jwt, cors and the rest of community-maden middleware by clicking [here](https://github.com/iris-contrib/middleware) +### Routing, Grouping, Dynamic Path Parameters, "Macros" and Custom Context + +- [Overview](routing/overview/main.go) +- [Basic](routing/basic/main.go) +- [Custom HTTP Errors](routing/http-errors/main.go) +- [Dynamic Path](routing/dynamic-path/main.go) +- [Reverse routing](routing/reverse/main.go) +- [Custom wrapper](routing/custom-wrapper/main.go) +- Custom Context + * [Method Overriding](routing/custom-context/method-overriding/main.go) + * [New Implementation](routing/custom-context/new-implementation/main.go) +- [Route State](routing/route-state/main.go) + +### Subdomains + +- [Single](subdomains/single/main.go) +- [Multi](subdomains/multi/main.go) +- [Wildcard](subdomains/wildcard/main.go) +- [WWW](subdomains/www/main.go) + +### Convert `http.Handler/HandlerFunc` + +- [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) + +### View + +| Engine | Declaration | +| -----------|-------------| +| template/html | `iris.HTML(...)` | +| django | `iris.Django(...)` | +| handlebars | `iris.Handlebars(...)` | +| amber | `iris.Amber(...)` | +| pug(jade) | `iris.Pug(...)` | + +- [Overview](view/overview/main.go) +- [Hi](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) +- [Embedding Templates Into App Executable File](view/embedding-templates-into-app/main.go) + +### Authentication + +- [Basic Authentication](authentication/basicauth/main.go) +- [OAUth2](authentication/oauth2/main.go) +- [JWT](https://github.com/iris-contrib/middleware/blob/master/jwt/_example/main.go) +- [Sessions](#sessions) + +### File Server + +- [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) +- [Send/Force-Download Files](file-server/send-files/main.go) +- Single Page Applications + * [Single Page Application](file-server/single-page-application/basic/main.go) + * [Embedded Single Page Application](file-server/single-page-application/embedded-single-page-application/main.go) + +### How to Read from `context.Request() *http.Request` + +- [Bind JSON](http_request/read-json/main.go) +- [Bind Form](http_request/read-form/main.go) +- [Upload/Read Files](http_request/upload-files/main.go) + +> The `context.Request()` returns the same *http.Request you already know, these examples show some places where the Context uses this object. Besides that you can use it as you did before iris. + +### How to Write to `context.ResponseWriter() http.ResponseWriter` + +- [Text, Markdown, HTML, JSON, JSONP, XML, Binary](http_responsewriter/write-rest/main.go) +- [Stream Writer](http_responsewriter/stream-writer/main.go) +- [Transactions](http_responsewriter/transactions/main.go) + +> The `context.ResponseWriter()` returns an enchament version of a http.ResponseWriter, these examples show some places where the Context uses this object. Besides that you can use it as you did before iris. + +### Miscellaneous + +- [Request Logger](http_request/request-logger/main.go) +- [Localization and Internationalization](miscellaneous/i18n/main.go) +- [Recovery](miscellaneous/recover/main.go) +- [Profiling (pprof)](miscellaneous/pprof/main.go) +- [Internal Application File Logger](miscellaneous/file-logger/main.go) + +#### More + +https://github.com/kataras/iris/tree/master/middleware#third-party-handlers + +### Testing + +The `httptest` package is your way for end-to-end HTTP testing, it uses the httpexpect library created by our friend, [gavv](https://github.com/gavv). + +[Example](testing/httptest/main_test.go) + +### Caching + +iris cache library lives on its own package: [https://github.com/kataras/iris/tree/master/cache](https://github.com/kataras/iris/tree/master/cache) **it contains examples** + +### Sessions + +iris session manager lives on its own package: [https://github.com/kataras/iris/tree/master/sessions](https://github.com/kataras/iris/tree/master/sessions) **it contains examples** + +> You're free to use your own favourite sessions package if you'd like so. + +### Websockets + +iris websocket library lives on its own package: [https://github.com/kataras/iris/tree/master/websocket](https://github.com/kataras/iris/tree/master/websocket) **it contains examples** + +> You're free to use your own favourite websockets package if you'd like so. + +### Typescript Automation Tools + +typescript automation tools have their own repository: [https://github.com/kataras/iris/tree/master/typescript](https://github.com/kataras/iris/tree/master/typescript) **it contains examples** + +> I'd like to tell you that you can use your favourite but I don't think you will find such a thing anywhere else. + +### Hey, You! + +Developers should read the [godocs](https://godoc.org/github.com/kataras/iris) for a better understanding. + +Psst, I almost forgot; do not forget to [star or watch](https://github.com/kataras/iris/stargazers) the project in order to stay updated with the latest tech trends, it never takes more than a second! -> Do not forget to [star or watch the project](https://github.com/kataras/iris/stargazers) in order to stay updated with the latest tech trends, it takes some seconds for the sake of go! -> Developers should read the official [documentation](https://godoc.org/github.com/kataras/iris) in depth, for deep understanding. diff --git a/_examples/advanced/url-shortener/shortener.go b/_examples/advanced/url-shortener/shortener.go deleted file mode 100644 index dd6419ba..00000000 --- a/_examples/advanced/url-shortener/shortener.go +++ /dev/null @@ -1,4 +0,0 @@ -package main - -// Version is the current version of the url-shortener package. -const Version = "0.0.1" diff --git a/_examples/authentication/README.md b/_examples/authentication/README.md new file mode 100644 index 00000000..c8287e6a --- /dev/null +++ b/_examples/authentication/README.md @@ -0,0 +1,6 @@ +# Authentication + +- [Basic Authentication](basicauth/main.go) +- [OAUth2](oauth2/main.go) +- [JWT](https://github.com/iris-contrib/middleware/blob/master/jwt/_example/main.go) +- [Sessions](https://github.com/kataras/iris/tree/master/_examples/#sessions) \ No newline at end of file diff --git a/_examples/beginner/basicauth/main.go b/_examples/authentication/basicauth/main.go similarity index 95% rename from _examples/beginner/basicauth/main.go rename to _examples/authentication/basicauth/main.go index 779fefca..6ad5b275 100644 --- a/_examples/beginner/basicauth/main.go +++ b/_examples/authentication/basicauth/main.go @@ -8,7 +8,7 @@ import ( "github.com/kataras/iris/middleware/basicauth" ) -func main() { +func newApp() *iris.Application { app := iris.New() authConfig := basicauth.Config{ @@ -40,6 +40,11 @@ func main() { needAuth.Get("/settings", h) } + return app +} + +func main() { + app := newApp() // open http://localhost:8080/admin app.Run(iris.Addr(":8080")) } diff --git a/_examples/authentication/basicauth/main_test.go b/_examples/authentication/basicauth/main_test.go new file mode 100644 index 00000000..2e123c0c --- /dev/null +++ b/_examples/authentication/basicauth/main_test.go @@ -0,0 +1,29 @@ +package main + +import ( + "testing" + + "github.com/kataras/iris/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/intermediate/oauth2/main.go b/_examples/authentication/oauth2/main.go similarity index 96% rename from _examples/intermediate/oauth2/main.go rename to _examples/authentication/oauth2/main.go index f592f8f2..18ea831f 100644 --- a/_examples/intermediate/oauth2/main.go +++ b/_examples/authentication/oauth2/main.go @@ -1,7 +1,7 @@ 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: +// can be used with iris but at this example we will see the markbates' goth: // // $ go get github.com/markbates/goth/... // @@ -26,8 +26,8 @@ import ( "github.com/kataras/iris" "github.com/kataras/iris/context" + "github.com/kataras/iris/sessions" - "github.com/kataras/iris/view" "github.com/gorilla/securecookie" // optionally, used for session's encoder/decoder @@ -70,6 +70,24 @@ import ( "github.com/markbates/goth/providers/yammer" ) +var sessionsManager *sessions.Sessions + +func init() { + // attach a session manager + cookieName := "mycustomsessionid" + // 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 := []byte("the-big-and-secret-fash-key-here") + blockKey := []byte("lot-secret-of-characters-big-too") + secureCookie := securecookie.New(hashKey, blockKey) + + sessionsManager = sessions.New(sessions.Config{ + Cookie: cookieName, + Encode: secureCookie.Encode, + Decode: secureCookie.Decode, + }) +} + // These are some function helpers that you may use if you want // GetProviderName is a function used to get the name of a provider @@ -97,7 +115,7 @@ var GetProviderName = func(ctx context.Context) (string, error) { } /* -BeginAuthHandler is a convienence handler for starting the authentication process. +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". @@ -146,8 +164,8 @@ func GetAuthURL(ctx context.Context) (string, error) { if err != nil { return "", err } - - ctx.Session().Set(providerName, sess.Marshal()) + session := sessionsManager.Start(ctx) + session.Set(providerName, sess.Marshal()) return url, nil } @@ -191,8 +209,8 @@ var CompleteUserAuth = func(ctx context.Context) (goth.User, error) { if err != nil { return goth.User{}, err } - - value := ctx.Session().GetString(providerName) + session := sessionsManager.Start(ctx) + value := session.GetString(providerName) if value == "" { return goth.User{}, errors.New("session value for " + providerName + " not found") } @@ -214,7 +232,7 @@ var CompleteUserAuth = func(ctx context.Context) (goth.User, error) { return goth.User{}, err } - ctx.Session().Set(providerName, sess.Marshal()) + session.Set(providerName, sess.Marshal()) return provider.FetchUser(sess) } @@ -224,8 +242,8 @@ func Logout(ctx context.Context) error { if err != nil { return err } - - ctx.Session().Delete(providerName) + session := sessionsManager.Start(ctx) + session.Delete(providerName) return nil } @@ -341,21 +359,7 @@ func main() { app := iris.New() // attach and build our templates - app.AttachView(view.HTML("./templates", ".html")) - - // attach a session manager - cookieName := "mycustomsessionid" - // 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 := []byte("the-big-and-secret-fash-key-here") - blockKey := []byte("lot-secret-of-characters-big-too") - secureCookie := securecookie.New(hashKey, blockKey) - sessManager := sessions.New(sessions.Config{ - Cookie: cookieName, - Encode: secureCookie.Encode, - Decode: secureCookie.Decode, - }) - app.AttachSessionManager(sessManager) + app.RegisterView(iris.HTML("./templates", ".html")) // start of the router diff --git a/_examples/intermediate/oauth2/templates/index.html b/_examples/authentication/oauth2/templates/index.html similarity index 100% rename from _examples/intermediate/oauth2/templates/index.html rename to _examples/authentication/oauth2/templates/index.html diff --git a/_examples/intermediate/oauth2/templates/user.html b/_examples/authentication/oauth2/templates/user.html similarity index 100% rename from _examples/intermediate/oauth2/templates/user.html rename to _examples/authentication/oauth2/templates/user.html diff --git a/_examples/beginner/e-mail/main.go b/_examples/beginner/e-mail/main.go deleted file mode 100644 index 9ee65ec0..00000000 --- a/_examples/beginner/e-mail/main.go +++ /dev/null @@ -1,87 +0,0 @@ -package main - -import ( - "bytes" - - "github.com/kataras/go-mailer" - - "github.com/kataras/iris" - "github.com/kataras/iris/context" - "github.com/kataras/iris/view" -) - -func main() { - - app := iris.New() - app.AttachView(view.HTML("./templates", ".html")) - - // change these to your own settings - cfg := mailer.Config{ - Host: "smtp.mailgun.org", - Username: "postmaster@sandbox661c307650f04e909150b37c0f3b2f09.mailgun.org", - Password: "38304272b8ee5c176d5961dc155b2417", - Port: 587, - } - // change these to your e-mail to check if that works - - // create the service - mailService := mailer.New(cfg) - - var to = []string{"kataras2006@hotmail.com"} - - // standalone - - //mailService.Send("iris e-mail test subject", "outside of context before server's listen!", to...) - - //inside handler - app.Get("/send", func(ctx context.Context) { - content := `

Hello From Iris web framework



This is the rich message body ` - - err := mailService.Send("iris e-mail just t3st subject", content, to...) - - if err != nil { - ctx.HTML(" Problem while sending the e-mail: " + err.Error()) - } else { - ctx.HTML("

SUCCESS

") - } - }) - - // send a body by template - app.Get("/send/template", func(ctx context.Context) { - // we will not use ctx.View - // because we don't want to render to the client - // we need the templates' parsed result as raw bytes - // so we make use of the bytes.Buffer which is an io.Writer - // which being expected on app.View parameter first. - // - // the rest of the parameters are the same and the behavior is the same as ctx.View, - // except the 'where to render' - buff := &bytes.Buffer{} - - // View executes and writes the result of a template file to the writer. - // - // First parameter is the writer to write the parsed template. - // Second parameter is the relative, to templates directory, template filename, including extension. - // Third parameter is the layout, can be empty string. - // Forth parameter is the bindable data to the template, can be nil. - // - // Use context.View to render templates to the client instead. - // Returns an error on failure, otherwise nil. - app.View(buff, "body.html", "", context.Map{ - "Message": " his is the rich message body sent by a template!!", - "Footer": "The footer of this e-mail!", - }) - content := buff.String() - - err := mailService.Send("iris e-mail just t3st subject", content, to...) - - if err != nil { - ctx.StatusCode(iris.StatusBadRequest) - ctx.HTML(" Sent failed with error: " + err.Error()) - } else { - ctx.HTML("

SUCCESS

") - } - }) - - app.Run(iris.Addr(":8080")) -} diff --git a/_examples/beginner/e-mail/templates/mail_body.html b/_examples/beginner/e-mail/templates/mail_body.html deleted file mode 100644 index 1e59b733..00000000 --- a/_examples/beginner/e-mail/templates/mail_body.html +++ /dev/null @@ -1,7 +0,0 @@ -

Hello From Iris web framework

-
-
- {{.Message}} -
- - {{.Footer}} diff --git a/_examples/beginner/favicon/static/favicons/iris_favicon_48_48.ico b/_examples/beginner/favicon/static/favicons/iris_favicon_48_48.ico deleted file mode 100644 index c370da518ec542579b7cc0d5d30f4778b4a96318..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 zcmeHOd2p50760;HNB{!~AhNgwD|W0M6=&Sqp@rI3TCD}AT5D@d+Zj9R*hO1s+FA`T z9a|JFi=v_+0>XO%S(L2^%A#S>B7{987$_lO&Gx>x<-K?M`LT>nUnjy z``vrax#ym9?z!iFF^oLJX_S;0sD~I2<{QRMhG7gDV*PfZeHPk=4U7K1(lBo8Zx~mh z4_;w})sD(A(C=IqPkFx6<8s{RSzwF@lqt9!sPoD^^YNR&XP4u5c)lL*9e`MAnQj!A zZ(q7R@X2Em*gZ}Hm46fO%CCrb)in~>@h1iE>TAVZItcAo*Lz&fhg;t?@;$CR#nCvy zDDb{}%kqf#sIY|U-WLCspG)}kRtX(ltiWgg=ATJ$-!tOdG^Ppkf8=o&7#_F7XdhoE zkzlj?*Z^I##7#{oxZO@+G<{tw(9B9xN@ZInbc1jW*5teH;QHN zbA7dq@7fN{QXb5u11I`F{IStEp7X!?orw3C1ouA=95bZBb+uGYDUra2?@I84A4v7I z5^dwV;DP5Q;;F$Lu63P!&~Os?9Pu2|clP)LlS{<^-U!8UaBAFm&>$_+ByE4iy#{%q zT*#Zv|8DSqY-Vq%pVeFE!Z|7bm}3NU<(g6UlmTTynIy_@=fr_Rpe*C%7lEu}@y2V0Wv*}X_sf(6Q zIv=Kvs;)w3WBLnSrcP70RmY+0)OqT@Z3wgp*oQ-Hgtn4sKWbA^`=YJU=3smF_^#F> z7+DA)CIZdM20XHii-YxeCe#8rmVRakU?hP4Nedm%`I007kJ|~K<17Z00xmmC7zqE< zgSuz?!X$zT4WLK*zXu$}n8Rto?L3S=QE;^PbK}`+uSLBd@NdB5fYKIs5A~-BBs=^> zKlUl}EbI>bFBS8h%aZ}^p7KJ0egpEvv#1wsMS;)S=El47d?6nEjsRyRaF4+l2Yqt7 zqV%yxc~^Z^!i|*@F^>Qa$H5T^!cUXFxum}Y{_|T2HLR6z>p=xRvyJaO?tJloc#rNE z=Hfo&<0SBp^^_ZV>3E3IUp-7Bfz#?s;{SwBtQT|HVDYZ_GWMI5a5N$7HS4%%zGEMA z*(J(f=;%^0mk$LGMgwTvrgX}aT|*}N5&uc?y*E<#S*!2^>m{Pf}QnZPHl3oPI>+qf6AurGCh zV_CR&c^3A-+BlwSad%JB$8_`~fivP=`!(^c`v&%hM)>}1;$41)G{2cAM_=tFRa5&Z z@R@CV$3E=KF&xWhX*0$5!CmCJ3bekID1&tLRd#O`WB;59!Ft92!5F1eJ*}^N^3TDt z{T~-A@R@DofqmGwt!Ac}7;CRtsD8D0-s&BJK0c)M;_M+E{lKoLV24B04noIP!RCtO z@XNiGHfd@c^X$ObZ@yz6_ND!BY+zTJu0bT&qI!h=Y#HbklBQi!(hmp4xA9KE9Zjd^2Hw}+B6Y9!P&#{_yGR<}>?Y=-9)Js^c|pGHpM0UdV;}bA7>@O- zO`lQvv`^2%o+j2{WLEm-e_^{Lnf3BWu&|&$$v;p{)b*HvHZ)X z+aD|D_MM=y&wLz9j!keCg=Z z4=R1`4cH^NS4hazy~F$NS?)j3BfDg#?=V_ix;H+>y^?z-OR)N7OV`h0|ILMdoD438 z3^tpH87P19Or2m%Vee%*#~)E&mNR|CM$vV+4Lme3c47>rdVrWJm-x%n_-ioE;_Gd) zu{x9d+xJ4~qQ;7d84)`&hGg!6F(qTmtnnveRN8Q=7?!auW86glW7D>4#;E|r%!r*C zLo=3UObz<@jXjj{cai#M`e;6D8)I|E$eChz#e-NsZvVIG#@C41jh4mKNBAY{EWtAc z&la@+oHtUQKdf^|<}=JujOSS=o?O~z&@ozF)C-)c+)kXW&iZ^-3LHG^rKR~rdSM&K zdc!H}kvag30F*|d7&W0V3Qp8np%#4zO;PZpCWHV;O^}e&rpr79Ql!Q&0kJ&AQ2aPw z&P(9D1d=2`zXccycoHxUKwth@fQ|c)!2eVD-(i5FW|v`ju{U(J02lfH9v}da&j~`H z!#vO$z*x+q#DiGF8@;RO%v>-&j(_gOMsC1;yTg1lj7`}Wd%iWEa$5noL6#ea>1I+m z_kwX2|E|0MW4u7+6Np#FXM_OW<6Z&uqjTV#5p#Ig+=$rdSBisp_{=sd9zh@F*MZn^ zAo4?3AvV#xpgoUjydUjz8+j)7_*_dn|3*ObJ8iA@R+6^n{eZ?vjGM{}#JA!5h%1-j ztbGcHf^hpdJhP4O*oS=?SD8x(;4J?F&cbt$Q~G*rUf`vUb7*q)giLc=htBdJKC@jI z(;{Zne3HhN0pzr@lnDB;FLO}TQOZBuxD$6g$2BkFLEa>~o;9d%1{j`2*<+&4@UM9| zPC6eXKbK^?pfX zcjyywTJ6ZG;a*jB%5uRXUEw?SVP74?GvJ2X6&G>pIuRG|Wh?Hr#<`uxz}rpaFH^~te_h|G?eYFl z3v`1y3B8AKTmGsKC{i52i61n%`sYlgSfb!#BJvZ z`7XF`BIeAqp|c)vJ$X!5a^YWd%?o>~9)wO-D0$`s_3R4$Xq;CN%Q-RclL*X5v5)#4 z-swd1kD-$vT6(vrr-bTPDPI2JSy}Uf+*jPr@t6zzY3e;87yg;|wfx1Mnu{lmz|KFb zUNBe8dxJ!t6MwT0`%;%URtxg_%!v~>X~dVnF4!{d$L-t#-urfyf7%@HdnmhL)f6QW zF=5Lquz#ZM=!{-TyjS85fRH!`_SH3j@8^90OYp!HZ6giazS&M6Y~7zZS}=!iQqHML zHu;BJs<78yf_P{TUj5n#W(8hd5EqW5omK=B@X zttiI(j){YGEr1VeZuw6W&&OZysrNGW965$-;Q#11ic7zd0B+JCt@sky`4^l2x$r|N z?@Y7tud)U`-}+GymLF@}f|zBv;;nh1Z!BL*8*NejMtlZ)G~eQ0YQ^K9PA}1UGPlYx zs*t8|69So z*SqEh#04E=N%ft?jc=k@!l$-CCMCGnD~S0R`kFKF@4SBt?Zi9}A*QLpJ=y*VSdRhF zD{HODf2=QX@Y@|0;#U3>d?;g6jaghqD|jECf&by)zg1%qjEOHf^N;zM$c2V>Jkrkp zzB2JHrCm5>FZ8C<{6m(MDP;@ZZ_U8}HC!XTr^1@Wmt_6frE5QLL(`=L_>xQ{B(zMa+|>^(eFQMbYSs0{qWHXJqP;=WkZZSm}@P0_Y!V=U?H z_sA=V>0zrT&XIwQqa;{y7v|Yt;Is8ki*wwLJ;1VG^vl71xd+7Y&m2Rd`D$DNy6{c# zQQTMHv*Jsx{&NrR`cvi!=VflK7i{bfP&X34_C6vzb?_lmRAN!&@xZ`3%7wBpb2Ha*sz;XlrfZv@R_)FXezPc)~YxH{xdZts{s+4Hoe2N*+s zV*a1cXv;N!63=^A_-C%D#f@*1^~{VM681B-rqUJubq&h%jHU(5VLL|y zkke*NldrjMp1Ya*wsU9HG0mSNr=D~E9N#E}>fXu3y1wfw|D1za?l8Oy`Hip6fp-g* zZ6e0!x2gPo#hMq}u@)WfFYv!1IFqEizu?^m??AHNli&`7cPj7+jGt4+pX4=Nfj>2* zobiQ&K4~Sux+9DE80&nlbK@GR|Dg{`0A0Tv`=%b-CH^@t-l>5ObnSXn2ybByg^J2`yXO%S(L2^%A#S>B7{987$_lO&Gx>x<-K?M`LT>nUnjy z``vrax#ym9?z!iFF^oLJX_S;0sD~I2<{QRMhG7gDV*PfZeHPk=4U7K1(lBo8Zx~mh z4_;w})sD(A(C=IqPkFx6<8s{RSzwF@lqt9!sPoD^^YNR&XP4u5c)lL*9e`MAnQj!A zZ(q7R@X2Em*gZ}Hm46fO%CCrb)in~>@h1iE>TAVZItcAo*Lz&fhg;t?@;$CR#nCvy zDDb{}%kqf#sIY|U-WLCspG)}kRtX(ltiWgg=ATJ$-!tOdG^Ppkf8=o&7#_F7XdhoE zkzlj?*Z^I##7#{oxZO@+G<{tw(9B9xN@ZInbc1jW*5teH;QHN zbA7dq@7fN{QXb5u11I`F{IStEp7X!?orw3C1ouA=95bZBb+uGYDUra2?@I84A4v7I z5^dwV;DP5Q;;F$Lu63P!&~Os?9Pu2|clP)LlS{<^-U!8UaBAFm&>$_+ByE4iy#{%q zT*#Zv|8DSqY-Vq%pVeFE!Z|7bm}3NU<(g6UlmTTynIy_@=fr_Rpe*C%7lEu}@y2V0Wv*}X_sf(6Q zIv=Kvs;)w3WBLnSrcP70RmY+0)OqT@Z3wgp*oQ-Hgtn4sKWbA^`=YJU=3smF_^#F> z7+DA)CIZdM20XHii-YxeCe#8rmVRakU?hP4Nedm%`I007kJ|~K<17Z00xmmC7zqE< zgSuz?!X$zT4WLK*zXu$}n8Rto?L3S=QE;^PbK}`+uSLBd@NdB5fYKIs5A~-BBs=^> zKlUl}EbI>bFBS8h%aZ}^p7KJ0egpEvv#1wsMS;)S=El47d?6nEjsRyRaF4+l2Yqt7 zqV%yxc~^Z^!i|*@F^>Qa$H5T^!cUXFxum}Y{_|T2HLR6z>p=xRvyJaO?tJloc#rNE z=Hfo&<0SBp^^_ZV>3E3IUp-7Bfz#?s;{SwBtQT|HVDYZ_GWMI5a5N$7HS4%%zGEMA z*(J(f=;%^0mk$LGMgwTvrgX}aT|*}N5&uc?y*E<#S*!2^>m{Pf}QnZPHl3oPI>+qf6AurGCh zV_CR&c^3A-+BlwSad%JB$8_`~fivP=`!(^c`v&%hM)>}1;$41)G{2cAM_=tFRa5&Z z@R@CV$3E=KF&xWhX*0$5!CmCJ3bekID1&tLRd#O`WB;59!Ft92!5F1eJ*}^N^3TDt z{T~-A@R@DofqmGwt!Ac}7;CRtsD8D0-s&BJK0c)M;_M+E{lKoLV24B04noIP!RCtO z@XNiGHfd@c^X$ObZ@yz6_ND!BY+zTJu0bT&qI!h=Y#HbklBQi!(hmp4xA9KE9Zjd^2Hw}+B6Y9!P&#{_yGR<}>?Y=-9)Js^c|pGHpM0UdV;}bA7>@O- zO`lQvv`^2%o+j2{WLEm-e_^{Lnf3BWu&|&$$v;p{)b*HvHZ)X z+aD|D_MM=y&wLz9j!keCg=Z z4=R1`4cH^NS4hazy~F$NS?)j3BfDg#?=V_ix;H+>y^?z-OR)N7OV`h0|ILMdoD438 z3^tpH87P19Or2m%Vee%*#~)E&mNR|CM$vV+4Lme3c47>rdVrWJm-x%n_-ioE;_Gd) zu{x9d+xJ4~qQ;7d84)`&hGg!6F(qTmtnnveRN8Q=7?!auW86glW7D>4#;E|r%!r*C zLo=3UObz<@jXjj{cai#M`e;6D8)I|E$eChz#e-NsZvVIG#@C41jh4mKNBAY{EWtAc z&la@+oHtUQKdf^|<}=JujOSS=o?O~z&@ozF)C-)c+)kXW&iZ^-3LHG^rKR~rdSM&K zdc!H}kvag30F*|d7&W0V3Qp8np%#4zO;PZpCWHV;O^}e&rpr79Ql!Q&0kJ&AQ2aPw z&P(9D1d=2`zXccycoHxUKwth@fQ|c)!2eVD-(i5FW|v`ju{U(J02lfH9v}da&j~`H z!#vO$z*x+q#DiGF8@;RO%v>-&j(_gOMsC1;yTg1lj7`}Wd%iWEa$5noL6#ea>1I+m z_kwX2|E|0MW4u7+6Np#FXM_OW<6Z&uqjTV#5p#Ig+=$rdSBisp_{=sd9zh@F*MZn^ zAo4?3AvV#xpgoUjydUjz8+j)7_*_dn|3*ObJ8iA@R+6^n{eZ?vjGM{}#JA!5h%1-j ztbGcHf^hpdJhP4O*oS=?SD8x(;4J?F&cbt$Q~G*rUf`vUb7*q)giLc=htBdJKC@jI z(;{Zne3HhN0pzr@lnDB;FLO}TQOZBuxD$6g$2BkFLEa>~o;9d%1{j`2*<+&4@UM9| zPC6eXKbK^?pfX zcjyywTJ6ZG;a*jB%5uRXUEw?SVP74?GvJ2X6&G>pIuRG|Wh?Hr#<`uxz}rpaFH^~te_h|G?eYFl z3v`1y3B8AKTmGsKC{i52i61n%`sYlgSfb!#BJvZ z`7XF`BIeAqp|c)vJ$X!5a^YWd%?o>~9)wO-D0$`s_3R4$Xq;CN%Q-RclL*X5v5)#4 z-swd1kD-$vT6(vrr-bTPDPI2JSy}Uf+*jPr@t6zzY3e;87yg;|wfx1Mnu{lmz|KFb zUNBe8dxJ!t6MwT0`%;%URtxg_%!v~>X~dVnF4!{d$L-t#-urfyf7%@HdnmhL)f6QW zF=5Lquz#ZM=!{-TyjS85fRH!`_SH3j@8^90OYp!HZ6giazS&M6Y~7zZS}=!iQqHML zHu;BJs<78yf_P{TUj5n#W(8hd5EqW5omK=B@X zttiI(j){YGEr1VeZuw6W&&OZysrNGW965$-;Q#11ic7zd0B+JCt@sky`4^l2x$r|N z?@Y7tud)U`-}+GymLF@}f|zBv;;nh1Z!BL*8*NejMtlZ)G~eQ0YQ^K9PA}1UGPlYx zs*t8|69So z*SqEh#04E=N%ft?jc=k@!l$-CCMCGnD~S0R`kFKF@4SBt?Zi9}A*QLpJ=y*VSdRhF zD{HODf2=QX@Y@|0;#U3>d?;g6jaghqD|jECf&by)zg1%qjEOHf^N;zM$c2V>Jkrkp zzB2JHrCm5>FZ8C<{6m(MDP;@ZZ_U8}HC!XTr^1@Wmt_6frE5QLL(`=L_>xQ{B(zMa+|>^(eFQMbYSs0{qWHXJqP;=WkZZSm}@P0_Y!V=U?H z_sA=V>0zrT&XIwQqa;{y7v|Yt;Is8ki*wwLJ;1VG^vl71xd+7Y&m2Rd`D$DNy6{c# zQQTMHv*Jsx{&NrR`cvi!=VflK7i{bfP&X34_C6vzb?_lmRAN!&@xZ`3%7wBpb2Ha*sz;XlrfZv@R_)FXezPc)~YxH{xdZts{s+4Hoe2N*+s zV*a1cXv;N!63=^A_-C%D#f@*1^~{VM681B-rqUJubq&h%jHU(5VLL|y zkke*NldrjMp1Ya*wsU9HG0mSNr=D~E9N#E}>fXu3y1wfw|D1za?l8Oy`Hip6fp-g* zZ6e0!x2gPo#hMq}u@)WfFYv!1IFqEizu?^m??AHNli&`7cPj7+jGt4+pX4=Nfj>2* zobiQ&K4~Sux+9DE80&nlbK@GR|Dg{`0A0Tv`=%b-CH^@t-l>5ObnSXn2ybByg^J2`yXO%S(L2^%A#S>B7{987$_lO&Gx>x<-K?M`LT>nUnjy z``vrax#ym9?z!iFF^oLJX_S;0sD~I2<{QRMhG7gDV*PfZeHPk=4U7K1(lBo8Zx~mh z4_;w})sD(A(C=IqPkFx6<8s{RSzwF@lqt9!sPoD^^YNR&XP4u5c)lL*9e`MAnQj!A zZ(q7R@X2Em*gZ}Hm46fO%CCrb)in~>@h1iE>TAVZItcAo*Lz&fhg;t?@;$CR#nCvy zDDb{}%kqf#sIY|U-WLCspG)}kRtX(ltiWgg=ATJ$-!tOdG^Ppkf8=o&7#_F7XdhoE zkzlj?*Z^I##7#{oxZO@+G<{tw(9B9xN@ZInbc1jW*5teH;QHN zbA7dq@7fN{QXb5u11I`F{IStEp7X!?orw3C1ouA=95bZBb+uGYDUra2?@I84A4v7I z5^dwV;DP5Q;;F$Lu63P!&~Os?9Pu2|clP)LlS{<^-U!8UaBAFm&>$_+ByE4iy#{%q zT*#Zv|8DSqY-Vq%pVeFE!Z|7bm}3NU<(g6UlmTTynIy_@=fr_Rpe*C%7lEu}@y2V0Wv*}X_sf(6Q zIv=Kvs;)w3WBLnSrcP70RmY+0)OqT@Z3wgp*oQ-Hgtn4sKWbA^`=YJU=3smF_^#F> z7+DA)CIZdM20XHii-YxeCe#8rmVRakU?hP4Nedm%`I007kJ|~K<17Z00xmmC7zqE< zgSuz?!X$zT4WLK*zXu$}n8Rto?L3S=QE;^PbK}`+uSLBd@NdB5fYKIs5A~-BBs=^> zKlUl}EbI>bFBS8h%aZ}^p7KJ0egpEvv#1wsMS;)S=El47d?6nEjsRyRaF4+l2Yqt7 zqV%yxc~^Z^!i|*@F^>Qa$H5T^!cUXFxum}Y{_|T2HLR6z>p=xRvyJaO?tJloc#rNE z=Hfo&<0SBp^^_ZV>3E3IUp-7Bfz#?s;{SwBtQT|HVDYZ_GWMI5a5N$7HS4%%zGEMA z*(J(f=;%^0mk$LGMgwTvrgX}aT|*}N5&uc?y*E<#S*!2^>m{Pf}QnZPHl3oPI>+qf6AurGCh zV_CR&c^3A-+BlwSad%JB$8_`~fivP=`!(^c`v&%hM)>}1;$41)G{2cAM_=tFRa5&Z z@R@CV$3E=KF&xWhX*0$5!CmCJ3bekID1&tLRd#O`WB;59!Ft92!5F1eJ*}^N^3TDt z{T~-A@R@DofqmGwt!Ac}7;CRtsD8D0-s&BJK0c)M;_M+E{lKoLV24B04noIP!RCtO z@XNiGHfd@c^X$ObZ@yz6_ND!BY+zTJu0bT&qI!h=Y#HbklBQi!(hmp4xA9KE9Zjd^2Hw}+B6Y9!P&#{_yGR<}>?Y=-9)Js^c|pGHpM0UdV;}bA7>@O- zO`lQvv`^2%o+j2{WLEm-e_^{Lnf3BWu&|&$$v;p{)b*HvHZ)X z+aD|D_MM=y&wLz9j!keCg=Z z4=R1`4cH^NS4hazy~F$NS?)j3BfDg#?=V_ix;H+>y^?z-OR)N7OV`h0|ILMdoD438 z3^tpH87P19Or2m%Vee%*#~)E&mNR|CM$vV+4Lme3c47>rdVrWJm-x%n_-ioE;_Gd) zu{x9d+xJ4~qQ;7d84)`&hGg!6F(qTmtnnveRN8Q=7?!auW86glW7D>4#;E|r%!r*C zLo=3UObz<@jXjj{cai#M`e;6D8)I|E$eChz#e-NsZvVIG#@C41jh4mKNBAY{EWtAc z&la@+oHtUQKdf^|<}=JujOSS=o?O~z&@ozF)C-)c+)kXW&iZ^-3LHG^rKR~rdSM&K zdc!H}kvag30F*|d7&W0V3Qp8np%#4zO;PZpCWHV;O^}e&rpr79Ql!Q&0kJ&AQ2aPw z&P(9D1d=2`zXccycoHxUKwth@fQ|c)!2eVD-(i5FW|v`ju{U(J02lfH9v}da&j~`H z!#vO$z*x+q#DiGF8@;RO%v>-&j(_gOMsC1;yTg1lj7`}Wd%iWEa$5noL6#ea>1I+m z_kwX2|E|0MW4u7+6Np#FXM_OW<6Z&uqjTV#5p#Ig+=$rdSBisp_{=sd9zh@F*MZn^ zAo4?3AvV#xpgoUjydUjz8+j)7_*_dn|3*ObJ8iA@R+6^n{eZ?vjGM{}#JA!5h%1-j ztbGcHf^hpdJhP4O*oS=?SD8x(;4J?F&cbt$Q~G*rUf`vUb7*q)giLc=htBdJKC@jI z(;{Zne3HhN0pzr@lnDB;FLO}TQOZBuxD$6g$2BkFLEa>~o;9d%1{j`2*<+&4@UM9| zPC6eXKbK^?pfX zcjyywTJ6ZG;a*jB%5uRXUEw?SVP74?GvJ2X6&G>pIuRG|Wh?Hr#<`uxz}rpaFH^~te_h|G?eYFl z3v`1y3B8AKTmGsKC{i52i61n%`sYlgSfb!#BJvZ z`7XF`BIeAqp|c)vJ$X!5a^YWd%?o>~9)wO-D0$`s_3R4$Xq;CN%Q-RclL*X5v5)#4 z-swd1kD-$vT6(vrr-bTPDPI2JSy}Uf+*jPr@t6zzY3e;87yg;|wfx1Mnu{lmz|KFb zUNBe8dxJ!t6MwT0`%;%URtxg_%!v~>X~dVnF4!{d$L-t#-urfyf7%@HdnmhL)f6QW zF=5Lquz#ZM=!{-TyjS85fRH!`_SH3j@8^90OYp!HZ6giazS&M6Y~7zZS}=!iQqHML zHu;BJs<78yf_P{TUj5n#W(8hd5EqW5omK=B@X zttiI(j){YGEr1VeZuw6W&&OZysrNGW965$-;Q#11ic7zd0B+JCt@sky`4^l2x$r|N z?@Y7tud)U`-}+GymLF@}f|zBv;;nh1Z!BL*8*NejMtlZ)G~eQ0YQ^K9PA}1UGPlYx zs*t8|69So z*SqEh#04E=N%ft?jc=k@!l$-CCMCGnD~S0R`kFKF@4SBt?Zi9}A*QLpJ=y*VSdRhF zD{HODf2=QX@Y@|0;#U3>d?;g6jaghqD|jECf&by)zg1%qjEOHf^N;zM$c2V>Jkrkp zzB2JHrCm5>FZ8C<{6m(MDP;@ZZ_U8}HC!XTr^1@Wmt_6frE5QLL(`=L_>xQ{B(zMa+|>^(eFQMbYSs0{qWHXJqP;=WkZZSm}@P0_Y!V=U?H z_sA=V>0zrT&XIwQqa;{y7v|Yt;Is8ki*wwLJ;1VG^vl71xd+7Y&m2Rd`D$DNy6{c# zQQTMHv*Jsx{&NrR`cvi!=VflK7i{bfP&X34_C6vzb?_lmRAN!&@xZ`3%7wBpb2Ha*sz;XlrfZv@R_)FXezPc)~YxH{xdZts{s+4Hoe2N*+s zV*a1cXv;N!63=^A_-C%D#f@*1^~{VM681B-rqUJubq&h%jHU(5VLL|y zkke*NldrjMp1Ya*wsU9HG0mSNr=D~E9N#E}>fXu3y1wfw|D1za?l8Oy`Hip6fp-g* zZ6e0!x2gPo#hMq}u@)WfFYv!1IFqEizu?^m??AHNli&`7cPj7+jGt4+pX4=Nfj>2* zobiQ&K4~Sux+9DE80&nlbK@GR|Dg{`0A0Tv`=%b-CH^@t-l>5ObnSXn2ybByg^J2`y Hello world!
") - }) - app.Run(iris.Addr(":8080")) -} diff --git a/_examples/beginner/listening/listen-unix/main.go b/_examples/beginner/listening/listen-unix/main.go deleted file mode 100644 index b699b136..00000000 --- a/_examples/beginner/listening/listen-unix/main.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - "github.com/kataras/iris" - "github.com/kataras/iris/core/nettools" -) - -func main() { - app := iris.New() - - l, err := nettools.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/beginner/write-json/main.go b/_examples/beginner/write-json/main.go deleted file mode 100644 index 6d6cf6ff..00000000 --- a/_examples/beginner/write-json/main.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "github.com/kataras/iris" - "github.com/kataras/iris/context" -) - -// User bind struct -type User struct { - Firstname string `json:"firstname"` - Lastname string `json:"lastname"` - City string `json:"city"` - Age int `json:"age"` -} - -func main() { - app := iris.New() - - app.Post("/decode", func(ctx context.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) - }) - - app.Get("/encode", func(ctx context.Context) { - peter := User{ - Firstname: "John", - Lastname: "Doe", - City: "Neither FBI knows!!!", - Age: 25, - } - - ctx.StatusCode(iris.StatusOK) - ctx.JSON(peter) - }) - - app.Run(iris.Addr(":8080")) -} diff --git a/_examples/beginner/configuration/basic/main.go b/_examples/configuration/from-configuration-structure/main.go similarity index 72% rename from _examples/beginner/configuration/basic/main.go rename to _examples/configuration/from-configuration-structure/main.go index acb9be7d..3c7fbdc5 100644 --- a/_examples/beginner/configuration/basic/main.go +++ b/_examples/configuration/from-configuration-structure/main.go @@ -2,16 +2,19 @@ package main import ( "github.com/kataras/iris" + "github.com/kataras/iris/context" ) func main() { app := iris.New() - + app.Get("/", func(ctx context.Context) { + ctx.HTML("Hello!") + }) // [...] // Good when you want to modify the whole configuration. app.Run(iris.Addr(":8080"), iris.WithConfiguration(iris.Configuration{ // default configuration: - DisableBanner: false, + DisableStartupLog: false, DisableInterruptHandler: false, DisablePathCorrection: false, EnablePathEscape: false, @@ -22,7 +25,6 @@ func main() { Charset: "UTF-8", })) - // or before run: - // app.Configure(iris.WithConfiguration(...)) - // app.Run(iris.Addr(":8080")) + // or before Run: + // app.Configure(iris.WithConfiguration(iris.Configuration{...})) } diff --git a/_examples/beginner/configuration/from-toml-file/configs/iris.tml b/_examples/configuration/from-toml-file/configs/ion.tml similarity index 89% rename from _examples/beginner/configuration/from-toml-file/configs/iris.tml rename to _examples/configuration/from-toml-file/configs/ion.tml index be14d235..34230c4c 100644 --- a/_examples/beginner/configuration/from-toml-file/configs/iris.tml +++ b/_examples/configuration/from-toml-file/configs/ion.tml @@ -6,4 +6,4 @@ TimeFormat = "Mon, 01 Jan 2006 15:04:05 GMT" Charset = "UTF-8" [Other] - MyServerName = "Iris" + MyServerName = "iris" diff --git a/_examples/beginner/configuration/from-toml-file/main.go b/_examples/configuration/from-toml-file/main.go similarity index 79% rename from _examples/beginner/configuration/from-toml-file/main.go rename to _examples/configuration/from-toml-file/main.go index c5b22d2c..2046e749 100644 --- a/_examples/beginner/configuration/from-toml-file/main.go +++ b/_examples/configuration/from-toml-file/main.go @@ -2,11 +2,15 @@ package main import ( "github.com/kataras/iris" + "github.com/kataras/iris/context" ) func main() { app := iris.New() + app.Get("/", func(ctx context.Context) { + ctx.HTML("Hello!") + }) // [...] // Good when you have two configurations, one for development and a different one for production use. diff --git a/_examples/beginner/configuration/from-yaml-file/configs/iris.yml b/_examples/configuration/from-yaml-file/configs/ion.yml similarity index 100% rename from _examples/beginner/configuration/from-yaml-file/configs/iris.yml rename to _examples/configuration/from-yaml-file/configs/ion.yml diff --git a/_examples/beginner/configuration/from-yaml-file/main.go b/_examples/configuration/from-yaml-file/main.go similarity index 79% rename from _examples/beginner/configuration/from-yaml-file/main.go rename to _examples/configuration/from-yaml-file/main.go index 930ca8e6..206d11fd 100644 --- a/_examples/beginner/configuration/from-yaml-file/main.go +++ b/_examples/configuration/from-yaml-file/main.go @@ -2,11 +2,14 @@ package main import ( "github.com/kataras/iris" + "github.com/kataras/iris/context" ) func main() { app := iris.New() - + app.Get("/", func(ctx context.Context) { + ctx.HTML("Hello!") + }) // [...] // Good when you have two configurations, one for development and a different one for production use. diff --git a/_examples/beginner/configuration/functional/main.go b/_examples/configuration/functional/main.go similarity index 59% rename from _examples/beginner/configuration/functional/main.go rename to _examples/configuration/functional/main.go index b13890f1..13dee581 100644 --- a/_examples/beginner/configuration/functional/main.go +++ b/_examples/configuration/functional/main.go @@ -2,15 +2,20 @@ package main import ( "github.com/kataras/iris" + "github.com/kataras/iris/context" ) func main() { app := iris.New() - + app.Get("/", func(ctx context.Context) { + ctx.HTML("Hello!") + }) // [...] // Good when you want to change some of the configuration's field. - // I use that method :) + // Prefix: "With", code editors will help you navigate through all + // configuration options without even a glitch to the documentation. + app.Run(iris.Addr(":8080"), iris.WithoutBanner, iris.WithCharset("UTF-8")) // or before run: diff --git a/_examples/beginner/convert-handlers/negroni-like/main.go b/_examples/convert-handlers/negroni-like/main.go similarity index 82% rename from _examples/beginner/convert-handlers/negroni-like/main.go rename to _examples/convert-handlers/negroni-like/main.go index c4a1f973..3770951d 100644 --- a/_examples/beginner/convert-handlers/negroni-like/main.go +++ b/_examples/convert-handlers/negroni-like/main.go @@ -5,13 +5,12 @@ import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/core/handlerconv" ) func main() { app := iris.New() - irisMiddleware := handlerconv.FromStdWithNext(negronilikeTestMiddleware) - app.Use(irisMiddleware) + ionMiddleware := iris.FromStd(negronilikeTestMiddleware) + app.Use(ionMiddleware) // Method GET: http://localhost:8080/ app.Get("/", func(ctx context.Context) { @@ -41,3 +40,6 @@ func negronilikeTestMiddleware(w http.ResponseWriter, r *http.Request, next http w.WriteHeader(iris.StatusBadRequest) w.Write([]byte("Bad request")) } + +// Look "routing/custom-context" if you want to convert a custom handler with a custom Context +// to a context.Handler. diff --git a/_examples/beginner/convert-handlers/nethttp/main.go b/_examples/convert-handlers/nethttp/main.go similarity index 73% rename from _examples/beginner/convert-handlers/nethttp/main.go rename to _examples/convert-handlers/nethttp/main.go index 995d67b5..c79d9473 100644 --- a/_examples/beginner/convert-handlers/nethttp/main.go +++ b/_examples/convert-handlers/nethttp/main.go @@ -5,13 +5,12 @@ import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/core/handlerconv" ) func main() { app := iris.New() - irisMiddleware := handlerconv.FromStd(nativeTestMiddleware) - app.Use(irisMiddleware) + ionMiddleware := iris.FromStd(nativeTestMiddleware) + app.Use(ionMiddleware) // Method GET: http://localhost:8080/ app.Get("/", func(ctx context.Context) { @@ -31,3 +30,6 @@ func main() { func nativeTestMiddleware(w http.ResponseWriter, r *http.Request) { println("Request path: " + r.URL.Path) } + +// Look "routing/custom-context" if you want to convert a custom handler with a custom Context +// to a context.Handler. diff --git a/_examples/beginner/file-server/basic/assets/css/main.css b/_examples/file-server/basic/assets/css/main.css similarity index 100% rename from _examples/beginner/file-server/basic/assets/css/main.css rename to _examples/file-server/basic/assets/css/main.css diff --git a/_examples/file-server/basic/assets/favicon.ico b/_examples/file-server/basic/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..961ef6dac80fa974d5bfeac74edcbf6797a7cd58 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x#lFaYI*xgi+L9|pukV-}_kLpIJv zQ+|#q=Ijh@h8zryI&2*6Mw}e2K=x7#W`=p@tPB&(xtKCdBpB*V1sJMK1Q|Na`B@rG z1(?I#%q()8)#sI4h=)em>M&S2JM(3F>bI4eiMKQva!V##sW7LXidT?ggs-)JM3aWP$dz37yar#qcC!5ioZpDxhah$9!u!Umsr=<*gHaFE6Qf{E}`U;Z>jK8QYs-^)A;&anZC&_Xo@Bod1;D zNS767cxq0`x4WHTDigV~%XjPI3cIfb2EwTf4BBjS>OD72%(L5UE5OI?>Fp%Fw8iuC z#C-doZK>Ax`}6D{*19Q8j*j+_s86suRAescR+;S)Jg3I(X{Ec$iF{|Z(2hLk(;05M zj){I24FCUcXP92&*>Pfy-~akBgSnMS4pXYU)K@uqxpJq6Sl1R=%c}gpW5AG;=;l`x zX>~HyO@B*zkY$4tFxAGXi!&6ZxH9A=x?0Syb9+?msums@?Bts1q2Fp??ZD*eW+xqL zqswZcYXD5qT8v&^j)v|ow(jSQB_ snFam>g^)3(is2;kALvyWmh1tR867~(f#$;mpcn{0U|{%(q#mRX0B!x$?f?J) literal 0 HcmV?d00001 diff --git a/_examples/beginner/file-server/basic/assets/js/jquery-2.1.1.js b/_examples/file-server/basic/assets/js/jquery-2.1.1.js similarity index 100% rename from _examples/beginner/file-server/basic/assets/js/jquery-2.1.1.js rename to _examples/file-server/basic/assets/js/jquery-2.1.1.js diff --git a/_examples/beginner/file-server/basic/main.go b/_examples/file-server/basic/main.go similarity index 100% rename from _examples/beginner/file-server/basic/main.go rename to _examples/file-server/basic/main.go diff --git a/_examples/beginner/file-server/embedding-files-into-app/assets/css/bootstrap.min.css b/_examples/file-server/embedding-files-into-app/assets/css/bootstrap.min.css similarity index 100% rename from _examples/beginner/file-server/embedding-files-into-app/assets/css/bootstrap.min.css rename to _examples/file-server/embedding-files-into-app/assets/css/bootstrap.min.css diff --git a/_examples/file-server/embedding-files-into-app/assets/favicon.ico b/_examples/file-server/embedding-files-into-app/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..961ef6dac80fa974d5bfeac74edcbf6797a7cd58 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x#lFaYI*xgi+L9|pukV-}_kLpIJv zQ+|#q=Ijh@h8zryI&2*6Mw}e2K=x7#W`=p@tPB&(xtKCdBpB*V1sJMK1Q|Na`B@rG z1(?I#%q()8)#sI4h=)em>M&S2JM(3F>bI4eiMKQva!V##sW7LXidT?ggs-)JM3aWP$dz37yar#qcC!5ioZpDxhah$9!u!Umsr=<*gHaFE6Qf{E}`U;Z>jK8QYs-^)A;&anZC&_Xo@Bod1;D zNS767cxq0`x4WHTDigV~%XjPI3cIfb2EwTf4BBjS>OD72%(L5UE5OI?>Fp%Fw8iuC z#C-doZK>Ax`}6D{*19Q8j*j+_s86suRAescR+;S)Jg3I(X{Ec$iF{|Z(2hLk(;05M zj){I24FCUcXP92&*>Pfy-~akBgSnMS4pXYU)K@uqxpJq6Sl1R=%c}gpW5AG;=;l`x zX>~HyO@B*zkY$4tFxAGXi!&6ZxH9A=x?0Syb9+?msums@?Bts1q2Fp??ZD*eW+xqL zqswZcYXD5qT8v&^j)v|ow(jSQB_ snFam>g^)3(is2;kALvyWmh1tR867~(f#$;mpcn{0U|{%(q#mRX0B!x$?f?J) literal 0 HcmV?d00001 diff --git a/_examples/beginner/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 similarity index 100% rename from _examples/beginner/file-server/embedding-files-into-app/assets/js/jquery-2.1.1.js rename to _examples/file-server/embedding-files-into-app/assets/js/jquery-2.1.1.js diff --git a/_examples/beginner/file-server/embedding-files-into-app/bindata.go b/_examples/file-server/embedding-files-into-app/bindata.go similarity index 95% rename from _examples/beginner/file-server/embedding-files-into-app/bindata.go rename to _examples/file-server/embedding-files-into-app/bindata.go index c24eb1b2..e5dd7e67 100644 --- a/_examples/beginner/file-server/embedding-files-into-app/bindata.go +++ b/_examples/file-server/embedding-files-into-app/bindata.go @@ -90,7 +90,7 @@ func assetsCssBootstrapMinCss() (*asset, error) { return a, nil } -var _assetsFaviconIco = []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") +var _assetsFaviconIco = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x62\x60\x60\x64\x60\x64\x10\x10\x00\xd1\x0a\x0c\x19\x2c\x0c\x0c\x62\x0c\x0c\x0c\x1a\x0c\x0c\x0c\x02\x0c\x0c\x0c\x0a\x0c\x10\x71\x30\x60\x61\x60\x10\xe6\x86\xe0\xe1\x02\x26\x1a\xb3\x30\x75\x18\xb2\x71\x36\x9a\xf2\x73\x4c\x31\x67\x67\x68\x33\xe4\x60\x68\xd4\x61\xe3\x68\x37\xe2\xe4\x68\x35\xe4\x60\x58\x6a\xc1\xcc\x30\xcf\x9c\x95\x61\x82\x39\x17\x53\xa6\x89\x04\x43\xbd\xa9\x00\x43\x95\x89\x20\x43\x87\x39\x3f\x4b\xa3\xa9\x00\x73\xb8\x9b\x99\x45\x8e\xb3\xfa\xbc\x72\x0b\xf1\xd0\x28\x5b\x1d\x06\x2b\x67\x67\xbe\x4c\x4f\xfd\xb6\x52\x33\xf1\x96\x26\x43\x6e\xc9\x64\x2b\x15\x06\x57\x3b\x0b\x91\x02\x33\x49\xb7\x16\x13\x1e\xbe\x78\x73\x65\x06\x5d\x47\x0f\xce\x0c\x5b\x95\xd4\x5a\x53\x41\xf5\x8e\x62\x8f\x92\x45\x35\x4e\x7f\xca\xec\x64\x33\xba\xd3\xad\x18\x32\x62\x9c\x13\x8a\x62\x6c\x5f\x36\xc5\x99\xbd\x2d\xb1\x92\x5a\xda\xa0\xc7\xa9\xe9\xe9\xe3\xac\x9d\xee\xa6\xbb\xa7\xca\x44\x30\xb2\x26\xc5\x81\xa1\x38\xcd\xc3\xa5\x35\xc1\xfc\x5e\x99\x91\x90\xcb\xd4\x1a\xaf\xa8\x8d\x6d\x2e\xdf\xca\x6c\xa4\x63\x2d\xdd\xdc\x84\x26\x96\xba\x1e\x2b\xf2\xd6\x99\x38\xb5\xda\x73\xc5\xfc\x0a\x87\x9f\xe5\x8e\x0a\x7b\xf3\x8c\x25\x1c\xcb\x92\x1c\x37\x96\x3b\x2b\x1d\x4a\x53\xe2\x56\x9d\xdf\xec\x33\x7d\x75\xbd\xe3\x8f\x22\x13\xb1\x98\x96\x22\x4f\xfb\xc5\xd5\x8e\x6f\x0a\xcd\x24\x42\x3a\xcb\xbc\x52\x97\xd5\x3b\x7f\xcc\xb7\x96\xf3\xf5\xf1\x75\x51\xde\x3a\xd1\xf7\xf2\x92\x6a\xc7\x2f\xe9\x06\x12\x5e\xf5\x79\x9e\xb1\xbd\x19\x56\xef\x72\x6d\x14\x17\x4d\xab\x74\x7f\xb0\xac\xce\xf9\x4f\xb9\x8d\x74\x59\x71\x86\xa7\xe6\x94\x7c\xbb\xdb\x19\xa6\x32\x91\x2b\xbb\x7c\xb7\x2e\xae\xb0\xfb\x5a\x60\x20\x9c\xca\xc0\xa0\xcd\x36\xa7\xde\x73\xd3\xc4\x3c\xbb\xcd\xb6\x02\x7c\xdc\x9e\xde\x4e\xd2\x4b\x5b\x3c\x2f\x4f\xcc\xb7\xff\xd9\x96\x6a\x7d\xbf\x3f\xcf\xfe\x61\xad\x9b\xd2\xe4\xe8\x68\x0f\x89\xfa\x04\xab\x43\x45\xe6\x92\x6e\x95\xd9\x1e\xc1\x73\x6a\xdc\x9e\x56\xba\xab\x9c\xc8\x77\x56\x0b\xed\xc8\x73\x3e\x9d\xe1\xa6\xeb\x98\xe8\x67\xc1\xf0\xff\xff\x76\x86\xe9\x35\x9e\x1d\x27\xe6\xf8\xfd\xaf\x0f\x33\x98\x5b\x99\xe4\x30\xa5\xca\x4b\x7d\x95\x93\x97\x2b\x77\x7a\x88\x75\x6d\x91\xb5\xac\xca\xff\x3b\x06\x0c\x39\x89\x6e\x7e\x45\x91\x56\x27\x53\xdd\xf4\xb7\xa4\x07\x59\x36\x38\x31\x30\xb0\xc4\xa9\x8b\x33\x14\xa5\xb8\x32\xe4\x25\xba\x5a\xcc\xaf\x73\x7b\x54\xec\xaa\x16\x1e\x19\xec\xe4\x9a\xe9\xa1\xdf\x6a\x60\xed\xc0\xe4\xe9\x66\x27\x1d\x6a\xa3\xcb\x6a\xa1\x6b\xc0\xc0\xc0\xa0\xc5\xe8\xe5\xe5\x68\xe8\xee\x62\xeb\x6e\x6f\x69\x26\xe8\xe8\x64\x07\x4e\x27\xaa\x06\x36\x0c\x36\x0e\x0e\x5c\xd1\x41\x0e\x41\x31\x36\x5a\xfc\x16\x0e\x8e\x3c\xae\x4e\x36\x72\x98\x29\xea\x3f\x1a\x1e\xde\xe0\xff\x7f\x04\x96\xec\x61\x60\x10\xe8\x60\x60\x10\xe0\x40\x53\x94\xc0\xc0\x20\x00\xc2\x0f\x18\x18\x7e\xfe\x47\xd5\x03\x08\x00\x00\xff\xff\x7d\x81\x4f\xc1\x7e\x04\x00\x00") func assetsFaviconIcoBytes() ([]byte, error) { return bindataRead( @@ -105,7 +105,7 @@ func assetsFaviconIco() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "assets/favicon.ico", size: 15086, mode: os.FileMode(438), modTime: time.Unix(1496517590, 0)} + info := bindataFileInfo{name: "assets/favicon.ico", size: 1150, mode: os.FileMode(438), modTime: time.Unix(1498241946, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -183,8 +183,8 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ "assets/css/bootstrap.min.css": assetsCssBootstrapMinCss, - "assets/favicon.ico": assetsFaviconIco, - "assets/js/jquery-2.1.1.js": assetsJsJquery211Js, + "assets/favicon.ico": assetsFaviconIco, + "assets/js/jquery-2.1.1.js": assetsJsJquery211Js, } // AssetDir returns the file names below a certain @@ -226,14 +226,15 @@ type bintree struct { Func func() (*asset, error) Children map[string]*bintree } + var _bintree = &bintree{nil, map[string]*bintree{ - "assets": &bintree{nil, map[string]*bintree{ - "css": &bintree{nil, map[string]*bintree{ - "bootstrap.min.css": &bintree{assetsCssBootstrapMinCss, map[string]*bintree{}}, + "assets": {nil, map[string]*bintree{ + "css": {nil, map[string]*bintree{ + "bootstrap.min.css": {assetsCssBootstrapMinCss, map[string]*bintree{}}, }}, - "favicon.ico": &bintree{assetsFaviconIco, map[string]*bintree{}}, - "js": &bintree{nil, map[string]*bintree{ - "jquery-2.1.1.js": &bintree{assetsJsJquery211Js, map[string]*bintree{}}, + "favicon.ico": {assetsFaviconIco, map[string]*bintree{}}, + "js": {nil, map[string]*bintree{ + "jquery-2.1.1.js": {assetsJsJquery211Js, map[string]*bintree{}}, }}, }}, }} @@ -284,4 +285,3 @@ func _filePath(dir, name string) string { cannonicalName := strings.Replace(name, "\\", "/", -1) return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) } - diff --git a/_examples/beginner/file-server/embedding-files-into-app/main.go b/_examples/file-server/embedding-files-into-app/main.go similarity index 100% rename from _examples/beginner/file-server/embedding-files-into-app/main.go rename to _examples/file-server/embedding-files-into-app/main.go diff --git a/_examples/beginner/file-server/embedding-files-into-app/main_test.go b/_examples/file-server/embedding-files-into-app/main_test.go similarity index 75% rename from _examples/beginner/file-server/embedding-files-into-app/main_test.go rename to _examples/file-server/embedding-files-into-app/main_test.go index fbd99898..10aaf0ec 100644 --- a/_examples/beginner/file-server/embedding-files-into-app/main_test.go +++ b/_examples/file-server/embedding-files-into-app/main_test.go @@ -3,10 +3,10 @@ package main import ( "io/ioutil" "path/filepath" + "runtime" "strings" "testing" - "github.com/kataras/iris" "github.com/kataras/iris/httptest" ) @@ -33,7 +33,11 @@ func (r resource) loadFromBase(dir string) string { panic(fullpath + " failed with error: " + err.Error()) } - return string(b) + result := string(b) + if runtime.GOOS != "windows" { + result = strings.Replace(result, "\n", "\r\n", -1) + } + return result } var urls = []resource{ @@ -49,12 +53,18 @@ func TestEmbeddingFilesIntoApp(t *testing.T) { 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 _, u := range urls { url := u.String() contents := u.loadFromBase("./assets") e.GET(url).Expect(). - Status(iris.StatusOK). + Status(httptest.StatusOK). Body().Equal(contents) } } diff --git a/_examples/beginner/favicon/main.go b/_examples/file-server/favicon/main.go similarity index 56% rename from _examples/beginner/favicon/main.go rename to _examples/file-server/favicon/main.go index 0525aa87..dd69477c 100644 --- a/_examples/beginner/favicon/main.go +++ b/_examples/file-server/favicon/main.go @@ -8,17 +8,17 @@ import ( func main() { app := iris.New() - // This will serve the ./static/favicons/iris_favicon_48_48.ico to: localhost:8080/favicon.ico - app.Favicon("./static/favicons/iris_favicon_48_48.ico") + // This will serve the ./static/favicons/favicon.ico to: localhost:8080/favicon.ico + app.Favicon("./static/favicons/favicon.ico.ico") - // app.Favicon("./static/favicons/iris_favicon_48_48.ico", "/favicon_48_48.ico") - // This will serve the ./static/favicons/iris_favicon_48_48.ico to: localhost:8080/favicon_48_48.ico + // app.Favicon("./static/favicons/favicon.ico.ico", "/favicon_16_16.ico") + // This will serve the ./static/favicons/favicon.ico.ico to: localhost:8080/favicon_16_16.ico app.Get("/", func(ctx context.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).`) + 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.Run(iris.Addr(":8080")) diff --git a/_examples/file-server/favicon/static/favicons/favicon.ico b/_examples/file-server/favicon/static/favicons/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..961ef6dac80fa974d5bfeac74edcbf6797a7cd58 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x#lFaYI*xgi+L9|pukV-}_kLpIJv zQ+|#q=Ijh@h8zryI&2*6Mw}e2K=x7#W`=p@tPB&(xtKCdBpB*V1sJMK1Q|Na`B@rG z1(?I#%q()8)#sI4h=)em>M&S2JM(3F>bI4eiMKQva!V##sW7LXidT?ggs-)JM3aWP$dz37yar#qcC!5ioZpDxhah$9!u!Umsr=<*gHaFE6Qf{E}`U;Z>jK8QYs-^)A;&anZC&_Xo@Bod1;D zNS767cxq0`x4WHTDigV~%XjPI3cIfb2EwTf4BBjS>OD72%(L5UE5OI?>Fp%Fw8iuC z#C-doZK>Ax`}6D{*19Q8j*j+_s86suRAescR+;S)Jg3I(X{Ec$iF{|Z(2hLk(;05M zj){I24FCUcXP92&*>Pfy-~akBgSnMS4pXYU)K@uqxpJq6Sl1R=%c}gpW5AG;=;l`x zX>~HyO@B*zkY$4tFxAGXi!&6ZxH9A=x?0Syb9+?msums@?Bts1q2Fp??ZD*eW+xqL zqswZcYXD5qT8v&^j)v|ow(jSQB_ snFam>g^)3(is2;kALvyWmh1tR867~(f#$;mpcn{0U|{%(q#mRX0B!x$?f?J) literal 0 HcmV?d00001 diff --git a/_examples/beginner/send-files/files/first.zip b/_examples/file-server/send-files/files/first.zip similarity index 100% rename from _examples/beginner/send-files/files/first.zip rename to _examples/file-server/send-files/files/first.zip diff --git a/_examples/beginner/send-files/main.go b/_examples/file-server/send-files/main.go similarity index 100% rename from _examples/beginner/send-files/main.go rename to _examples/file-server/send-files/main.go diff --git a/_examples/beginner/file-server/single-page-application/main.go b/_examples/file-server/single-page-application/basic/main.go similarity index 83% rename from _examples/beginner/file-server/single-page-application/main.go rename to _examples/file-server/single-page-application/basic/main.go index 16da3a5f..98982ace 100644 --- a/_examples/beginner/file-server/single-page-application/main.go +++ b/_examples/file-server/single-page-application/basic/main.go @@ -6,7 +6,7 @@ import ( "github.com/kataras/iris/view" ) -// same as embedding-single-page-application but without go-bindata, the files are "physical" stored in the +// same as embedded-single-page-application but without go-bindata, the files are "physical" stored in the // current system directory. var page = struct { @@ -15,7 +15,7 @@ var page = struct { func newApp() *iris.Application { app := iris.New() - app.AttachView(view.HTML("./public", ".html")) + app.RegisterView(view.HTML("./public", ".html")) app.Get("/", func(ctx context.Context) { ctx.ViewData("Page", page) diff --git a/_examples/beginner/file-server/single-page-application/main_test.go b/_examples/file-server/single-page-application/basic/main_test.go similarity index 89% rename from _examples/beginner/file-server/single-page-application/main_test.go rename to _examples/file-server/single-page-application/basic/main_test.go index 31187d95..59f1270f 100644 --- a/_examples/beginner/file-server/single-page-application/main_test.go +++ b/_examples/file-server/single-page-application/basic/main_test.go @@ -6,7 +6,6 @@ import ( "strings" "testing" - "github.com/kataras/iris" "github.com/kataras/iris/httptest" ) @@ -35,7 +34,8 @@ func (r resource) loadFromBase(dir string) string { panic(fullpath + " failed with error: " + err.Error()) } - return string(b) + result := string(b) + return result } var urls = []resource{ @@ -47,7 +47,7 @@ var urls = []resource{ func TestSPA(t *testing.T) { app := newApp() - e := httptest.New(t, app) + e := httptest.New(t, app, httptest.Debug(true)) for _, u := range urls { url := u.String() @@ -55,7 +55,7 @@ func TestSPA(t *testing.T) { contents = strings.Replace(contents, "{{ .Page.Title }}", page.Title, 1) e.GET(url).Expect(). - Status(iris.StatusOK). + Status(httptest.StatusOK). Body().Equal(contents) } } diff --git a/_examples/beginner/file-server/embedding-single-page-application/public/app.js b/_examples/file-server/single-page-application/basic/public/app.js similarity index 100% rename from _examples/beginner/file-server/embedding-single-page-application/public/app.js rename to _examples/file-server/single-page-application/basic/public/app.js diff --git a/_examples/beginner/file-server/embedding-single-page-application/public/css/main.css b/_examples/file-server/single-page-application/basic/public/css/main.css similarity index 100% rename from _examples/beginner/file-server/embedding-single-page-application/public/css/main.css rename to _examples/file-server/single-page-application/basic/public/css/main.css diff --git a/_examples/beginner/file-server/embedding-single-page-application/public/index.html b/_examples/file-server/single-page-application/basic/public/index.html similarity index 100% rename from _examples/beginner/file-server/embedding-single-page-application/public/index.html rename to _examples/file-server/single-page-application/basic/public/index.html diff --git a/_examples/beginner/file-server/embedding-single-page-application/bindata.go b/_examples/file-server/single-page-application/embedded-single-page-application/bindata.go similarity index 95% rename from _examples/beginner/file-server/embedding-single-page-application/bindata.go rename to _examples/file-server/single-page-application/embedded-single-page-application/bindata.go index 967d6ca1..6f2d1085 100644 --- a/_examples/beginner/file-server/embedding-single-page-application/bindata.go +++ b/_examples/file-server/single-page-application/embedded-single-page-application/bindata.go @@ -182,9 +182,9 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "public/app.js": publicAppJs, + "public/app.js": publicAppJs, "public/css/main.css": publicCssMainCss, - "public/index.html": publicIndexHtml, + "public/index.html": publicIndexHtml, } // AssetDir returns the file names below a certain @@ -226,13 +226,14 @@ type bintree struct { Func func() (*asset, error) Children map[string]*bintree } + var _bintree = &bintree{nil, map[string]*bintree{ - "public": &bintree{nil, map[string]*bintree{ - "app.js": &bintree{publicAppJs, map[string]*bintree{}}, - "css": &bintree{nil, map[string]*bintree{ - "main.css": &bintree{publicCssMainCss, map[string]*bintree{}}, + "public": {nil, map[string]*bintree{ + "app.js": {publicAppJs, map[string]*bintree{}}, + "css": {nil, map[string]*bintree{ + "main.css": {publicCssMainCss, map[string]*bintree{}}, }}, - "index.html": &bintree{publicIndexHtml, map[string]*bintree{}}, + "index.html": {publicIndexHtml, map[string]*bintree{}}, }}, }} @@ -282,4 +283,3 @@ func _filePath(dir, name string) string { cannonicalName := strings.Replace(name, "\\", "/", -1) return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) } - diff --git a/_examples/beginner/file-server/embedding-single-page-application/main.go b/_examples/file-server/single-page-application/embedded-single-page-application/main.go similarity index 91% rename from _examples/beginner/file-server/embedding-single-page-application/main.go rename to _examples/file-server/single-page-application/embedded-single-page-application/main.go index 7975201a..d01b3322 100644 --- a/_examples/beginner/file-server/embedding-single-page-application/main.go +++ b/_examples/file-server/single-page-application/embedded-single-page-application/main.go @@ -9,7 +9,7 @@ import ( // $ go get -u github.com/jteeuwen/go-bindata/... // $ go-bindata ./public/... // $ go build -// $ ./embedding-single-page-application +// $ ./embedded-single-page-application var page = struct { Title string @@ -17,7 +17,7 @@ var page = struct { func newApp() *iris.Application { app := iris.New() - app.AttachView(view.HTML("./public", ".html").Binary(Asset, AssetNames)) + app.RegisterView(view.HTML("./public", ".html").Binary(Asset, AssetNames)) app.Get("/", func(ctx context.Context) { ctx.ViewData("Page", page) diff --git a/_examples/beginner/file-server/embedding-single-page-application/main_test.go b/_examples/file-server/single-page-application/embedded-single-page-application/main_test.go similarity index 85% rename from _examples/beginner/file-server/embedding-single-page-application/main_test.go rename to _examples/file-server/single-page-application/embedded-single-page-application/main_test.go index f1bfdd29..2fe89b40 100644 --- a/_examples/beginner/file-server/embedding-single-page-application/main_test.go +++ b/_examples/file-server/single-page-application/embedded-single-page-application/main_test.go @@ -3,10 +3,10 @@ package main import ( "io/ioutil" "path/filepath" + "runtime" "strings" "testing" - "github.com/kataras/iris" "github.com/kataras/iris/httptest" ) @@ -34,8 +34,11 @@ func (r resource) loadFromBase(dir string) string { if err != nil { panic(fullpath + " failed with error: " + err.Error()) } - - return string(b) + result := string(b) + if runtime.GOOS != "windows" { + result = strings.Replace(result, "\n", "\r\n", -1) + } + return result } var urls = []resource{ @@ -55,7 +58,7 @@ func TestSPAEmbedded(t *testing.T) { contents = strings.Replace(contents, "{{ .Page.Title }}", page.Title, 1) e.GET(url).Expect(). - Status(iris.StatusOK). + Status(httptest.StatusOK). Body().Equal(contents) } } diff --git a/_examples/beginner/file-server/single-page-application/public/app.js b/_examples/file-server/single-page-application/embedded-single-page-application/public/app.js similarity index 100% rename from _examples/beginner/file-server/single-page-application/public/app.js rename to _examples/file-server/single-page-application/embedded-single-page-application/public/app.js diff --git a/_examples/beginner/file-server/single-page-application/public/css/main.css b/_examples/file-server/single-page-application/embedded-single-page-application/public/css/main.css similarity index 100% rename from _examples/beginner/file-server/single-page-application/public/css/main.css rename to _examples/file-server/single-page-application/embedded-single-page-application/public/css/main.css diff --git a/_examples/beginner/file-server/single-page-application/public/index.html b/_examples/file-server/single-page-application/embedded-single-page-application/public/index.html similarity index 100% rename from _examples/beginner/file-server/single-page-application/public/index.html rename to _examples/file-server/single-page-application/embedded-single-page-application/public/index.html diff --git a/_examples/hello-world/main.go b/_examples/hello-world/main.go new file mode 100644 index 00000000..ec5787ef --- /dev/null +++ b/_examples/hello-world/main.go @@ -0,0 +1,36 @@ +// +build go.1.8 + +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +func main() { + app := iris.Default() + + // Method: GET + // Resource: http://localhost:8080/ + app.Handle("GET", "/", func(ctx context.Context) { + ctx.HTML("Hello world!") + }) + + // same as app.Handle("GET", "/ping", [...]) + // Method: GET + // Resource: http://context:8080/ping + app.Get("/ping", func(ctx iris.Context) { + ctx.WriteString("pong") + }) + + // Method: GET + // Resource: http://localhost:8080/hello + app.Get("/hello", func(ctx context.Context) { + ctx.JSON(iris.Map{"message": "Hello iris web framework."}) + }) + + // http://localhost:8080 + // http://localhost:8080/ping + // http://localhost:8080/hello + app.Run(iris.Addr(":8080")) +} diff --git a/_examples/hello-world/main_go19.go b/_examples/hello-world/main_go19.go new file mode 100644 index 00000000..6a35f5cd --- /dev/null +++ b/_examples/hello-world/main_go19.go @@ -0,0 +1,35 @@ +// +build go1.9 + +package main + +import ( + "github.com/kataras/iris" +) + +func main() { + app := iris.Default() + + // Method: GET + // Resource: http://localhost:8080/ + app.Handle("GET", "/", func(ctx iris.Context) { + ctx.HTML("Hello world!") + }) + + // 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 web framework."}) + }) + + // http://localhost:8080 + // http://localhost:8080/ping + // http://localhost:8080/hello + app.Run(iris.Addr(":8080")) +} diff --git a/_examples/intermediate/custom-httpserver/iris-way/main.go b/_examples/http-listening/custom-httpserver/ion-way/main.go similarity index 84% rename from _examples/intermediate/custom-httpserver/iris-way/main.go rename to _examples/http-listening/custom-httpserver/ion-way/main.go index 04a3b7bc..1a5a61e1 100644 --- a/_examples/intermediate/custom-httpserver/iris-way/main.go +++ b/_examples/http-listening/custom-httpserver/ion-way/main.go @@ -18,7 +18,9 @@ func main() { ctx.Writef("Hello from %s", ctx.Path()) }) - srv := &http.Server{Addr: ":8080" /* Any custom fields here: Handler and ErrorLog are setted to the server automatically */} + // Any custom fields here. Handler and ErrorLog are setted to the server automatically + srv := &http.Server{Addr: ":8080"} + // http://localhost:8080/ // http://localhost:8080/mypath app.Run(iris.Server(srv)) // same as app.Run(iris.Addr(":8080")) diff --git a/_examples/intermediate/custom-httpserver/multi/main.go b/_examples/http-listening/custom-httpserver/multi/main.go similarity index 100% rename from _examples/intermediate/custom-httpserver/multi/main.go rename to _examples/http-listening/custom-httpserver/multi/main.go diff --git a/_examples/intermediate/custom-httpserver/std-way/main.go b/_examples/http-listening/custom-httpserver/std-way/main.go similarity index 89% rename from _examples/intermediate/custom-httpserver/std-way/main.go rename to _examples/http-listening/custom-httpserver/std-way/main.go index 0d4af2f5..3396f007 100644 --- a/_examples/intermediate/custom-httpserver/std-way/main.go +++ b/_examples/http-listening/custom-httpserver/std-way/main.go @@ -18,10 +18,8 @@ func main() { ctx.Writef("Hello from %s", ctx.Path()) }) - // call .Build before use the 'app' as an http.Handler on a custom http.Server - if err := app.Build(); err != nil { - panic(err) - } + // 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. diff --git a/_examples/beginner/listening/custom-listener/main.go b/_examples/http-listening/custom-listener/main.go similarity index 100% rename from _examples/beginner/listening/custom-listener/main.go rename to _examples/http-listening/custom-listener/main.go diff --git a/_examples/http-listening/custom-listener/unix-reuseport/main.go b/_examples/http-listening/custom-listener/unix-reuseport/main.go new file mode 100644 index 00000000..58391233 --- /dev/null +++ b/_examples/http-listening/custom-listener/unix-reuseport/main.go @@ -0,0 +1,45 @@ +// +build linux darwin dragonfly freebsd netbsd openbsd rumprun + +package main + +import ( + // Package tcplisten provides customizable TCP net.Listener with various + // performance-related options: + // + // - SO_REUSEPORT. 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. + // + // - TCP_DEFER_ACCEPT. This option expects the server reads from the accepted + // connection before writing to them. + // + // - TCP_FASTOPEN. See https://lwn.net/Articles/508865/ for details. + "github.com/valyala/tcplisten" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +// $ go get github.com/valyala/tcplisten +// $ go run main.go + +func main() { + app := iris.New() + + app.Get("/", func(ctx context.Context) { + ctx.HTML("Hello World!") + }) + + listenerCfg := tcplisten.Config{ + ReusePort: true, + DeferAccept: true, + FastOpen: true, + } + + l, err := listenerCfg.NewListener("tcp", ":8080") + if err != nil { + panic(err) + } + + app.Run(iris.Listener(l)) +} diff --git a/_examples/http-listening/custom-listener/unix-reuseport/main_windows.go b/_examples/http-listening/custom-listener/unix-reuseport/main_windows.go new file mode 100644 index 00000000..91c9720f --- /dev/null +++ b/_examples/http-listening/custom-listener/unix-reuseport/main_windows.go @@ -0,0 +1,7 @@ +// +build windows + +package main + +func main() { + panic("windows operating system does not support this feature") +} diff --git a/_examples/intermediate/graceful-shutdown/custom-notifier/main.go b/_examples/http-listening/graceful-shutdown/custom-notifier/main.go similarity index 70% rename from _examples/intermediate/graceful-shutdown/custom-notifier/main.go rename to _examples/http-listening/graceful-shutdown/custom-notifier/main.go index ac6b6d90..e808e5dd 100644 --- a/_examples/intermediate/graceful-shutdown/custom-notifier/main.go +++ b/_examples/http-listening/graceful-shutdown/custom-notifier/main.go @@ -13,10 +13,9 @@ import ( func main() { app := iris.New() - // output startup banner and error logs on os.Stdout app.Get("/", func(ctx context.Context) { - ctx.HTML("

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

") + ctx.HTML("

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

") }) go func() { @@ -33,16 +32,16 @@ func main() { ) select { case <-ch: - println("Shutdown the server gracefully...") + println("shutdown...") - timeout := 5 * time.Second // give the server 5 seconds to wait for idle connections. + timeout := 5 * 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. + // Start the server and disable the default interrupt handler in order to + // handle it clear and simple by our own, without any issues. app.Run(iris.Addr(":8080"), iris.WithoutInterruptHandler) } diff --git a/_examples/http-listening/graceful-shutdown/default-notifier/main.go b/_examples/http-listening/graceful-shutdown/default-notifier/main.go new file mode 100644 index 00000000..c99138f3 --- /dev/null +++ b/_examples/http-listening/graceful-shutdown/default-notifier/main.go @@ -0,0 +1,35 @@ +package main + +import ( + stdContext "context" + "time" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +// 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 := 5 * time.Second + ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout) + defer cancel() + // close all hosts + app.Shutdown(ctx) + }) + + app.Get("/", func(ctx context.Context) { + ctx.HTML("

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

") + }) + + // http://localhost:8080 + app.Run(iris.Addr(":8080"), iris.WithoutInterruptHandler) +} diff --git a/_examples/beginner/listening/listen-addr/main.go b/_examples/http-listening/listen-addr/main.go similarity index 86% rename from _examples/beginner/listening/listen-addr/main.go rename to _examples/http-listening/listen-addr/main.go index 1d5b8ce0..673057ca 100644 --- a/_examples/beginner/listening/listen-addr/main.go +++ b/_examples/http-listening/listen-addr/main.go @@ -9,7 +9,7 @@ func main() { app := iris.New() app.Get("/", func(ctx context.Context) { - ctx.HTML("

Index /

") + ctx.HTML("

Hello World!/

") }) if err := app.Run(iris.Addr(":8080")); err != nil { diff --git a/_examples/beginner/listening/listen-letsencrypt/main.go b/_examples/http-listening/listen-letsencrypt/main.go similarity index 88% rename from _examples/beginner/listening/listen-letsencrypt/main.go rename to _examples/http-listening/listen-letsencrypt/main.go index 9e6ecd91..e2f20741 100644 --- a/_examples/beginner/listening/listen-letsencrypt/main.go +++ b/_examples/http-listening/listen-letsencrypt/main.go @@ -22,7 +22,7 @@ func main() { }) // If http to https auto-redirect is one of your needs - // please look the code inside iris_deprecateed.go.ListenLETSENCRYPT to do it manually. + // please look the code inside ion_deprecateed.go.ListenLETSENCRYPT to do it manually. // NOTE: This may not work on local addresses like this, // use it on a real domain, because diff --git a/_examples/beginner/listening/listen-tls/main.go b/_examples/http-listening/listen-tls/main.go similarity index 100% rename from _examples/beginner/listening/listen-tls/main.go rename to _examples/http-listening/listen-tls/main.go diff --git a/_examples/beginner/listening/listen-tls/mycert.cert b/_examples/http-listening/listen-tls/mycert.cert similarity index 100% rename from _examples/beginner/listening/listen-tls/mycert.cert rename to _examples/http-listening/listen-tls/mycert.cert diff --git a/_examples/beginner/listening/listen-tls/mykey.key b/_examples/http-listening/listen-tls/mykey.key similarity index 100% rename from _examples/beginner/listening/listen-tls/mykey.key rename to _examples/http-listening/listen-tls/mykey.key diff --git a/_examples/http-listening/listen-unix/main.go b/_examples/http-listening/listen-unix/main.go new file mode 100644 index 00000000..6e8d69de --- /dev/null +++ b/_examples/http-listening/listen-unix/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/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)) +} + +// Look "custom-listener/unix-reuseport" too. diff --git a/_examples/http_request/README.md b/_examples/http_request/README.md new file mode 100644 index 00000000..98e7b09c --- /dev/null +++ b/_examples/http_request/README.md @@ -0,0 +1 @@ +The `context.Request()` returns the same *http.Request you already know, the examples show some places where the Context uses this object. Besides that you can use it as you did before iris. \ No newline at end of file diff --git a/_examples/beginner/read-form/main.go b/_examples/http_request/read-form/main.go similarity index 93% rename from _examples/beginner/read-form/main.go rename to _examples/http_request/read-form/main.go index 7a571728..6957bf42 100644 --- a/_examples/beginner/read-form/main.go +++ b/_examples/http_request/read-form/main.go @@ -17,7 +17,7 @@ func main() { app := iris.New() // set the view html template engine - app.AttachView(view.HTML("./templates", ".html").Reload(true)) + app.RegisterView(view.HTML("./templates", ".html").Reload(true)) app.Get("/", func(ctx context.Context) { if err := ctx.View("form.html"); err != nil { diff --git a/_examples/beginner/read-form/templates/form.html b/_examples/http_request/read-form/templates/form.html similarity index 100% rename from _examples/beginner/read-form/templates/form.html rename to _examples/http_request/read-form/templates/form.html diff --git a/_examples/beginner/read-json/main.go b/_examples/http_request/read-json/main.go similarity index 89% rename from _examples/beginner/read-json/main.go rename to _examples/http_request/read-json/main.go index cb7a5e1f..c565315d 100644 --- a/_examples/beginner/read-json/main.go +++ b/_examples/http_request/read-json/main.go @@ -31,7 +31,7 @@ func main() { // to the http://localhost:8080 with RAW BODY: /* { - "Name": "Iris-Go", + "Name": "iris-Go", "City": "New York", "Other": "Something here" } @@ -39,6 +39,6 @@ func main() { // and Content-Type to application/json // // The response should be: - // Received: &main.Company{Name:"Iris-Go", City:"New York", Other:"Something here"} + // Received: &main.Company{Name:"iris-Go", City:"New York", Other:"Something here"} app.Run(iris.Addr(":8080")) } diff --git a/_examples/beginner/request-logger/main.go b/_examples/http_request/request-logger/main.go similarity index 100% rename from _examples/beginner/request-logger/main.go rename to _examples/http_request/request-logger/main.go diff --git a/_examples/beginner/upload-files/main.go b/_examples/http_request/upload-files/main.go similarity index 96% rename from _examples/beginner/upload-files/main.go rename to _examples/http_request/upload-files/main.go index 5f686a78..f6ff3763 100644 --- a/_examples/beginner/upload-files/main.go +++ b/_examples/http_request/upload-files/main.go @@ -16,7 +16,7 @@ import ( func main() { app := iris.New() - app.AttachView(view.HTML("./templates", ".html")) + app.RegisterView(view.HTML("./templates", ".html")) // Serve the form.html to the user app.Get("/upload", func(ctx context.Context) { diff --git a/_examples/beginner/upload-files/templates/upload_form.html b/_examples/http_request/upload-files/templates/upload_form.html similarity index 100% rename from _examples/beginner/upload-files/templates/upload_form.html rename to _examples/http_request/upload-files/templates/upload_form.html diff --git a/_examples/http_responsewriter/README.md b/_examples/http_responsewriter/README.md new file mode 100644 index 00000000..4e743060 --- /dev/null +++ b/_examples/http_responsewriter/README.md @@ -0,0 +1 @@ +The `context.ResponseWriter()` returns an enchament version of a http.ResponseWriter, the examples show some places where the Context uses this object. Besides that you can use it as you did before iris. \ No newline at end of file diff --git a/_examples/beginner/stream-writer/main.go b/_examples/http_responsewriter/stream-writer/main.go similarity index 100% rename from _examples/beginner/stream-writer/main.go rename to _examples/http_responsewriter/stream-writer/main.go diff --git a/_examples/intermediate/transactions/main.go b/_examples/http_responsewriter/transactions/main.go similarity index 99% rename from _examples/intermediate/transactions/main.go rename to _examples/http_responsewriter/transactions/main.go index efd7246d..ce8fcd9d 100644 --- a/_examples/intermediate/transactions/main.go +++ b/_examples/http_responsewriter/transactions/main.go @@ -32,7 +32,7 @@ func main() { // 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 succesfuly, + // 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) diff --git a/_examples/intermediate/view/overview/main.go b/_examples/http_responsewriter/write-rest/main.go similarity index 51% rename from _examples/intermediate/view/overview/main.go rename to _examples/http_responsewriter/write-rest/main.go index 35da5d29..9443230c 100644 --- a/_examples/intermediate/view/overview/main.go +++ b/_examples/http_responsewriter/write-rest/main.go @@ -5,9 +5,16 @@ import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/view" ) +// User bind struct +type User struct { + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + City string `json:"city"` + Age int `json:"age"` +} + // ExampleXML just a test struct to view represents xml content-type type ExampleXML struct { XMLName xml.Name `xml:"example"` @@ -18,8 +25,32 @@ type ExampleXML struct { func main() { app := iris.New() - // Just some general restful render types, none of these has to do anything with templates. - app.Get("/binary", func(ctx context.Context) { // useful when you want force-download of contents of raw bytes form. + // Read + app.Post("/decode", func(ctx context.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) + }) + + // Write + app.Get("/encode", func(ctx context.Context) { + peter := User{ + Firstname: "John", + Lastname: "Doe", + City: "Neither FBI knows!!!", + Age: 25, + } + + ctx.StatusCode(iris.StatusOK) + // Manually setting a content type: ctx.ContentType("application/javascript") + ctx.JSON(peter) + }) + + // Other content types, + + app.Get("/binary", func(ctx context.Context) { + // useful when you want force-download of contents of raw bytes form. ctx.Binary([]byte("Some binary data here.")) }) @@ -40,38 +71,17 @@ func main() { }) app.Get("/markdown", func(ctx context.Context) { - ctx.Markdown([]byte("# Hello Dynamic Markdown -- Iris")) + ctx.Markdown([]byte("# Hello Dynamic Markdown -- iris")) }) + // http://localhost:8080/decode + // http://localhost:8080/encode // - - // - standard html | view.HTML(...) - // - django | view.Django(...) - // - pug(jade) | view.Pug(...) - // - handlebars | view.Handlebars(...) - // - amber | view.Amber(...) - // with default template funcs: - // - // - {{ urlpath "mynamedroute" "pathParameter_ifneeded" }} - // - {{ render "header.html" }} - // - {{ render_r "header.html" }} // partial relative path to current page - // - {{ yield }} - // - {{ current }} - app.AttachView(view.HTML("./templates", ".html")) - app.Get("/template", func(ctx context.Context) { - - ctx.ViewData("Name", "Iris") // the .Name inside the ./templates/hi.html - ctx.Gzip(true) // enable gzip for big files - ctx.View("hi.html") // render the template with the file name relative to the './templates' - - }) - // 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/template app.Run(iris.Addr(":8080")) } diff --git a/_examples/intermediate/cloud-editor/main.go b/_examples/intermediate/cloud-editor/main.go deleted file mode 100644 index 6348ece9..00000000 --- a/_examples/intermediate/cloud-editor/main.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "github.com/kataras/iris" - - "github.com/kataras/iris/typescript" // optionally - "github.com/kataras/iris/typescript/editor" -) - -func main() { - app := iris.New() - // adapt a router, order doesn't matters - - // optionally but good to have, I didn't put inside editor or the editor in the typescript compiler adaptors - // because you may use tools like gulp and you may use the editor without the typescript compiler adaptor. - // but if you need auto-compilation on .ts, we have a solution: - ts := typescript.New() - ts.Config.Dir = "./www/scripts/" - ts.Attach(app) // attach the typescript compiler adaptor - - editorConfig := editor.Config{ - Hostname: "localhost", - Port: 4444, - WorkingDir: "./www/scripts/", // "/path/to/the/client/side/directory/", - Username: "myusername", - Password: "mypassword", - } - e := editor.New(editorConfig) - e.Attach(app) // attach the editor - - app.StaticWeb("/", "./www") // serve the index.html - - app.Run(iris.Addr(":8080")) -} diff --git a/_examples/intermediate/graceful-shutdown/basic/main.go b/_examples/intermediate/graceful-shutdown/basic/main.go deleted file mode 100644 index 94f1385d..00000000 --- a/_examples/intermediate/graceful-shutdown/basic/main.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - stdContext "context" - "time" - - "github.com/kataras/iris" - "github.com/kataras/iris/context" - "github.com/kataras/iris/core/host" -) - -// Before continue, please read the below notes: -// -// Current version of Iris is auto-graceful on control+C/command+C -// or kill command sent or whenever app.Shutdown called. -// -// In order to add a custom interrupt handler(ctrl+c/cmd+c) or -// shutdown manually you have to "schedule a host supervisor's task" or -// use the core/host package manually or use a pure http.Server as we already seen at "custom-server" example. -// -// At this example, we will disable the interrupt handler and set our own interrupt handler. -func main() { - app := iris.New() - // output startup banner and error logs on os.Stdout - - app.Get("/", func(ctx context.Context) { - ctx.HTML("

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

") - }) - - // tasks are always running in their go-routine by-default. - // - // register custom interrupt handler, fires when ctrl+C/cmd+C pressed or kill command sent. - app.Scheduler.Schedule(host.OnInterrupt(func(proc host.TaskProcess) { - println("Shutdown the server gracefully...") - - timeout := 5 * time.Second // give the server 5 seconds to wait for idle connections. - ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout) - defer cancel() - proc.Host().Shutdown(ctx) // Shutdown the underline supervisor's server - })) - - // Start the server and disable the default interrupt handler in order to use our scheduled interrupt task. - app.Run(iris.Addr(":8080"), iris.WithoutInterruptHandler) -} - -// Note: -// You can just use an http.Handler with your own signal notify channel and do that as you did with the net/http -// package. I will not show this way, but you can find many examples on the internet. diff --git a/_examples/intermediate/graceful-shutdown/custom-host/main.go b/_examples/intermediate/graceful-shutdown/custom-host/main.go deleted file mode 100644 index 5c0fde64..00000000 --- a/_examples/intermediate/graceful-shutdown/custom-host/main.go +++ /dev/null @@ -1,72 +0,0 @@ -package main - -import ( - stdContext "context" - "net/http" - "time" - - "github.com/kataras/iris" - "github.com/kataras/iris/context" - "github.com/kataras/iris/core/host" -) - -// The difference from its parent main.go is that -// with a custom host we're able to call the host's shutdown -// and be notified about its .Shutdown call. -// Almost the same as before. -func main() { - app := iris.New() - // output startup banner and error logs on os.Stdout - - app.Get("/", func(ctx context.Context) { - ctx.HTML("

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

") - }) - - // build the framework when all routing-relative actions are declared. - if err := app.Build(); err != nil { - panic(err) - } - - // create our custom host supervisor by adapting - // a custom http server - srv := host.New(&http.Server{Addr: ":8080", Handler: app}) - - // tasks are always running in their go-routine by-default. - // - // register custom interrupt handler, fires when ctrl+C/cmd+C pressed or kill command sent, as we did before. - srv.Schedule(host.OnInterrupt(func(proc host.TaskProcess) { - println("Shutdown the server gracefully...") - - timeout := 5 * time.Second // give the server 5 seconds to wait for idle connections. - ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout) - defer cancel() - proc.Host().DeferFlow() // defer the exit, in order to catch the event - srv.Shutdown(ctx) // Shutdown the supervisor, only this way the proc.Host().Done will be notified - // the proc.Host().Shutdown, closes the underline server but no the supervisor. - // This behavior was choosen because is inneed for custom maintanance services - // (i.e restart server every week without loosing connections) which you can easly adapt with Iris. - })) - - // schedule a task to be notify when the server was closed by our interrutp handler, - // optionally ofcourse. - srv.ScheduleFunc(func(proc host.TaskProcess) { - select { - case <-proc.Host().Done(): // when .Host.Shutdown(ctx) called. - println("Server was closed.") - proc.Host().RestoreFlow() // Restore the flow in order to exit(continue after the srv.ListenAndServe) - } - }) - - // Start our custom host - println("Server is running at :8080") - srv.ListenAndServe() - // Go to the console and press ctrl+c(for windows and linux) or cmd+c for osx. - // The output should be: - // Server is running at:8080 - // Shutdown the server gracefully... - // Server was closed. -} - -// Note: -// You can just use an http.Handler with your own signal notify channel and do that as you did with the net/http -// package. I will not show this way, but you can find many examples on the internet. diff --git a/_examples/intermediate/i18n/locales/locale_el-GR.ini b/_examples/intermediate/i18n/locales/locale_el-GR.ini deleted file mode 100644 index 53258cff..00000000 --- a/_examples/intermediate/i18n/locales/locale_el-GR.ini +++ /dev/null @@ -1 +0,0 @@ -hi = Γεια, %s \ No newline at end of file diff --git a/_examples/intermediate/sessions/password-hashing/main.go b/_examples/intermediate/sessions/password-hashing/main.go deleted file mode 100644 index 9954565b..00000000 --- a/_examples/intermediate/sessions/password-hashing/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" - - "golang.org/x/crypto/bcrypt" -) - -func HashPassword(password string) (string, error) { - bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14) - return string(bytes), err -} - -func CheckPasswordHash(password, hash string) bool { - err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) - return err == nil -} - -func main() { - password := "secret" - hash, _ := HashPassword(password) // ignore error for the sake of simplicity - - fmt.Println("Password:", password) - fmt.Println("Hash: ", hash) - - match := CheckPasswordHash(password, hash) - fmt.Println("Match: ", match) -} diff --git a/_examples/intermediate/subdomains/multi/public/assets/images/test.ico b/_examples/intermediate/subdomains/multi/public/assets/images/test.ico deleted file mode 100644 index 5987ec74e8e2f8e51b648ca48c0eebba874f3eed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7414 zcmd5>3s98T6~2j1qBEIF+L)>=$kU?Hj+3UfcAPXb9TTQzGPL6)CMaoCEI3U^j8SSF zz@SpcChRV-3nanFn*~&K*=2c29%4`)%S%kG2BpDKQ+14OsT#hX^Y32#fqeirrT@&~ z?)~pM=R2=^|9dY&JRqivfB->lfGGA6!XSk3p_cnGQ-yf?Q6V0A#L>@uP>8~rLQJ3T z=xZOK@mWGlo95_;Q@?e#5Dz`%=-Wxx^pHr>0lr(n&vy?f440NyW{eR@=;O!A{NyFpPL^AKj%)ob4W6lsG{Omd%^f?%&)fT$NPApe~>=&Rqxb2VS% zeEnht|ID$cJ>ho{_BZbEi&AHzx%m{TOzRMxk%(mm8xE&GhF4bPAmUeL*b%z{+8x^v zy|M?JqK@OQwpmzF+mD3m9Q1d+MfihgKRU+~{!Mq-%6pv#_2y(WHP#?stHEMJ6CyL- z!6E%j#BX>VFR%CjF{`XFB<;mn>l|#ZK8$6xchPJP!kvLD`1^%CbXt_}vs)WH;!}K0 z=^uvo%z)%e%V zGYa;F)>w2{g2sZ+_2+i}Nqk4^hjQa0d{k{vV$s@m8lN4v;c~wNlYwtuu0>DXQo;=x z3pVrc{Byhh499oO(PG^k@Vdk~v6^Dg-53v!HLo)rl>uYLm3(U^d;R5a0Qp(!%2#ji z)a$RT=W(>k_h%Bnzy4$^yRSTxwf|u7|A+k-?>|c%`)-%tB3HFA!TgWfe>yTC#Q$Lb z9j4B?#8tPGdH#@gOy>DZ+8}Gvxi@yXKlPIT37`z;W1N#=xKHd+lM?%na z@F|?A`WepDJtbox+xJQKyp%7^2*dHpMEo{03+sNPMY-WIbXU8MWkZu+QX&SicU9~*xsVrE9 z+9L^w(Ce_;n1VQ4A=Xxp?Q*o6H6%Bxj#wAR@4oWKbwoOb)vdI_Oa7Jm zQPpp^l#{Io`H}U@W{k4Kjr_3rqX^O;l^q-pj_H`@?~NHk^gb*^1KlhpjbSxn9_D2k zmgNP3u3*J1B5&&q*+g85pIhj6dy&@vPTSFt+muH;(H0(~mK%%QdJCzp zeI@^G$zMWihP7lZ3mt-;Jk=%nrKqF!&qRH;4+H7U71~F*DLf$Y zX^ht@CEiXR8H01o=afeY<)SoA1LooTo6dXZ)fCoWteuU#{8((?8jVj%e2_)=AI&Yv zl&`t0uTR0Cb8&5145GDv!tb|bBcU=AS9^92-8rxx=HoMCUFmw%Rwv-=o&DeyC?l~8J~}mC@(~=9)8c`oR4WloIX$apkc<+5Lu{x2HGn_ z2PiHUA`R!lIi~o<^+n?{58Ntp#6x#jfhg3@r8}&7=(u-RXQ8nH{x0t=%|g<~DD2<8 z6=mJWWNZg`yoBO!Em(<^!c>^-NAXo#ta_YvP@Xju|3GwD=^pUjT@a744Q}o8=HE_c zaP#mO$C3VprL4JQh@+8WbgRP>dW&LXWVK|Q(P6uW>U^j^jr9Ij*?|beZ;M2x@eQ0l zxsL1Q7QO#Rxp4uq^W*SITdLf{@Ozd)Vd`wk!O;E05XMN{T?{_r;dkuMR3feoPyP^A zc#+ocdY3iy8u{{^Nw2xC-xon^Yc#s8LD$Hq>-q16!``@>*011e%wI_S#LN8Qq<+}L zxctvFj-^Ljo$sDw`IQe68^^QsXO31*kKLXqgKp%>0gG5PoDJV W=@vDeG%sl?%6Ov5Zu@DI)bSscpZ3!L diff --git a/_examples/intermediate/subdomains/multi/public/upload_resources/iris_favicon_32_32.ico b/_examples/intermediate/subdomains/multi/public/upload_resources/iris_favicon_32_32.ico deleted file mode 100644 index 5987ec74e8e2f8e51b648ca48c0eebba874f3eed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7414 zcmd5>3s98T6~2j1qBEIF+L)>=$kU?Hj+3UfcAPXb9TTQzGPL6)CMaoCEI3U^j8SSF zz@SpcChRV-3nanFn*~&K*=2c29%4`)%S%kG2BpDKQ+14OsT#hX^Y32#fqeirrT@&~ z?)~pM=R2=^|9dY&JRqivfB->lfGGA6!XSk3p_cnGQ-yf?Q6V0A#L>@uP>8~rLQJ3T z=xZOK@mWGlo95_;Q@?e#5Dz`%=-Wxx^pHr>0lr(n&vy?f440NyW{eR@=;O!A{NyFpPL^AKj%)ob4W6lsG{Omd%^f?%&)fT$NPApe~>=&Rqxb2VS% zeEnht|ID$cJ>ho{_BZbEi&AHzx%m{TOzRMxk%(mm8xE&GhF4bPAmUeL*b%z{+8x^v zy|M?JqK@OQwpmzF+mD3m9Q1d+MfihgKRU+~{!Mq-%6pv#_2y(WHP#?stHEMJ6CyL- z!6E%j#BX>VFR%CjF{`XFB<;mn>l|#ZK8$6xchPJP!kvLD`1^%CbXt_}vs)WH;!}K0 z=^uvo%z)%e%V zGYa;F)>w2{g2sZ+_2+i}Nqk4^hjQa0d{k{vV$s@m8lN4v;c~wNlYwtuu0>DXQo;=x z3pVrc{Byhh499oO(PG^k@Vdk~v6^Dg-53v!HLo)rl>uYLm3(U^d;R5a0Qp(!%2#ji z)a$RT=W(>k_h%Bnzy4$^yRSTxwf|u7|A+k-?>|c%`)-%tB3HFA!TgWfe>yTC#Q$Lb z9j4B?#8tPGdH#@gOy>DZ+8}Gvxi@yXKlPIT37`z;W1N#=xKHd+lM?%na z@F|?A`WepDJtbox+xJQKyp%7^2*dHpMEo{03+sNPMY-WIbXU8MWkZu+QX&SicU9~*xsVrE9 z+9L^w(Ce_;n1VQ4A=Xxp?Q*o6H6%Bxj#wAR@4oWKbwoOb)vdI_Oa7Jm zQPpp^l#{Io`H}U@W{k4Kjr_3rqX^O;l^q-pj_H`@?~NHk^gb*^1KlhpjbSxn9_D2k zmgNP3u3*J1B5&&q*+g85pIhj6dy&@vPTSFt+muH;(H0(~mK%%QdJCzp zeI@^G$zMWihP7lZ3mt-;Jk=%nrKqF!&qRH;4+H7U71~F*DLf$Y zX^ht@CEiXR8H01o=afeY<)SoA1LooTo6dXZ)fCoWteuU#{8((?8jVj%e2_)=AI&Yv zl&`t0uTR0Cb8&5145GDv!tb|bBcU=AS9^92-8rxx=HoMCUFmw%Rwv-=o&DeyC?l~8J~}mC@(~=9)8c`oR4WloIX$apkc<+5Lu{x2HGn_ z2PiHUA`R!lIi~o<^+n?{58Ntp#6x#jfhg3@r8}&7=(u-RXQ8nH{x0t=%|g<~DD2<8 z6=mJWWNZg`yoBO!Em(<^!c>^-NAXo#ta_YvP@Xju|3GwD=^pUjT@a744Q}o8=HE_c zaP#mO$C3VprL4JQh@+8WbgRP>dW&LXWVK|Q(P6uW>U^j^jr9Ij*?|beZ;M2x@eQ0l zxsL1Q7QO#Rxp4uq^W*SITdLf{@Ozd)Vd`wk!O;E05XMN{T?{_r;dkuMR3feoPyP^A zc#+ocdY3iy8u{{^Nw2xC-xon^Yc#s8LD$Hq>-q16!``@>*011e%wI_S#LN8Qq<+}L zxctvFj-^Ljo$sDw`IQe68^^QsXO31*kKLXqgKp%>0gG5PoDJV W=@vDeG%sl?%6Ov5Zu@DI)bSscpZ3!L diff --git a/_examples/intermediate/websockets/overview/main.go b/_examples/intermediate/websockets/overview/main.go deleted file mode 100644 index 4127596f..00000000 --- a/_examples/intermediate/websockets/overview/main.go +++ /dev/null @@ -1,100 +0,0 @@ -package main - -import ( - "fmt" // optional - - "github.com/kataras/iris" - "github.com/kataras/iris/context" - - "github.com/kataras/iris/view" - "github.com/kataras/iris/websocket" -) - -type clientPage struct { - Title string - Host string -} - -func main() { - app := iris.New() - app.AttachView(view.HTML("./templates", ".html")) // select the html engine to serve templates - - ws := websocket.New(websocket.Config{ - // the request path which the websocket client should listen/registered to the server, - // the endpoint is like a route. - Endpoint: "/my_endpoint", - // the client-side javascript static file path - // which will be served by Iris. - // default is /iris-ws.js - // if you change that you have to change the bottom of templates/client.html - // script tag: - ClientSourcePath: "/iris-ws.js", - // - // Set the timeouts, 0 means no timeout - // websocket has more configuration, go to ../../config.go for more: - // WriteTimeout: 0, - // ReadTimeout: 0, - // by-default all origins are accepted, you can change this behavior by setting: - // CheckOrigin: (r *http.Request ) bool {}, - // - // - // IDGenerator used to create (and later on, set) - // an ID for each incoming websocket connections (clients). - // The request is an argument which you can use to generate the ID (from headers for example). - // If empty then the ID is generated by DefaultIDGenerator: randomString(64): - // IDGenerator func(ctx context.Context) string {}, - }) - - ws.Attach(app) // adapt the websocket server, you can adapt more than one with different Endpoint - - app.StaticWeb("/js", "./static/js") // serve our custom javascript code - - app.Get("/", func(ctx context.Context) { - ctx.ViewData("", clientPage{"Client Page", ctx.Host()}) - ctx.View("client.html") - }) - - var myChatRoom = "room1" - - ws.OnConnection(func(c websocket.Connection) { - // Context returns the (upgraded) context.Context of this connection - // avoid using it, you normally don't need it, - // websocket has everything you need to authenticate the user BUT if it's necessary - // then you use it to receive user information, for example: from headers. - - // ctx := c.Context() - - // join to a room (optional) - c.Join(myChatRoom) - - c.On("chat", func(message string) { - if message == "leave" { - c.Leave(myChatRoom) - c.To(myChatRoom).Emit("chat", "Client with ID: "+c.ID()+" left from the room and cannot send or receive message to/from this room.") - c.Emit("chat", "You have left from the room: "+myChatRoom+" you cannot send or receive any messages from others inside that room.") - return - } - // to all except this connection -> - // c.To(websocket.Broadcast).Emit("chat", "Message from: "+c.ID()+"-> "+message) - // to all connected clients: c.To(websocket.All) - - // to the client itself -> - //c.Emit("chat", "Message from myself: "+message) - - //send the message to the whole room, - //all connections are inside this room will receive this message - c.To(myChatRoom).Emit("chat", "From: "+c.ID()+": "+message) - }) - - // or create a new leave event - // c.On("leave", func() { - // c.Leave(myChatRoom) - // }) - - c.OnDisconnect(func() { - fmt.Printf("Connection with ID: %s has been disconnected!\n", c.ID()) - }) - }) - - app.Run(iris.Addr(":8080")) -} diff --git a/_examples/intermediate/websockets/overview/static/js/chat.js b/_examples/intermediate/websockets/overview/static/js/chat.js deleted file mode 100644 index 920a2050..00000000 --- a/_examples/intermediate/websockets/overview/static/js/chat.js +++ /dev/null @@ -1,38 +0,0 @@ -var messageTxt; -var messages; - -$(function () { - - messageTxt = $("#messageTxt"); - messages = $("#messages"); - - - w = new Ws("ws://" + HOST + "/my_endpoint"); - w.OnConnect(function () { - console.log("Websocket connection established"); - }); - - w.OnDisconnect(function () { - appendMessage($("

Disconnected

")); - }); - - w.On("chat", function (message) { - appendMessage($("
" + message + "
")); - }); - - $("#sendBtn").click(function () { - w.Emit("chat", messageTxt.val().toString()); - messageTxt.val(""); - }); - -}) - - -function appendMessage(messageDiv) { - var theDiv = messages[0]; - var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight; - messageDiv.appendTo(messages); - if (doScroll) { - theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight; - } -} diff --git a/_examples/intermediate/websockets/overview/templates/client.html b/_examples/intermediate/websockets/overview/templates/client.html deleted file mode 100644 index 04ad2115..00000000 --- a/_examples/intermediate/websockets/overview/templates/client.html +++ /dev/null @@ -1,24 +0,0 @@ - - - -{{ .Title}} - - - -
- -
- - - - - - - - - - - diff --git a/_examples/beginner/file-logger/main.go b/_examples/miscellaneous/file-logger/main.go similarity index 80% rename from _examples/beginner/file-logger/main.go rename to _examples/miscellaneous/file-logger/main.go index 02d362f4..ac6f6a16 100644 --- a/_examples/beginner/file-logger/main.go +++ b/_examples/miscellaneous/file-logger/main.go @@ -9,7 +9,7 @@ import ( ) // get a filename based on the date, file logs works that way the most times -// but these are just a sugar, you can directly attach a new file logger with .AttachLogger(io.Writer) +// but these are just a sugar. func todayFilename() string { today := time.Now().Format("Jan 02 2006") return today + ".txt" @@ -32,18 +32,19 @@ func main() { app := iris.New() // attach the file as logger, remember, iris' app logger is just an io.Writer. - app.AttachLogger(f) + app.Logger().Out = newLogFile() app.Get("/", func(ctx context.Context) { // for the sake of simplicity, in order see the logs at the ./_today_.txt - ctx.Application().Log("Request: %s\r\n", ctx.Path()) + ctx.Application().Logger().Infoln("Request path: " + ctx.Path()) ctx.Writef("hello") }) // navigate to http://localhost:8080 // and open the ./logs.txt file if err := app.Run(iris.Addr(":8080"), iris.WithoutBanner); err != nil { - app.Log("Shutdown with error: %v\n", err) - + if err != iris.ErrServerClosed { + app.Logger().Warnln("Shutdown with error: " + err.Error()) + } } } diff --git a/_examples/miscellaneous/i18n/locales/locale_el-GR.ini b/_examples/miscellaneous/i18n/locales/locale_el-GR.ini new file mode 100644 index 00000000..a99e7fcc --- /dev/null +++ b/_examples/miscellaneous/i18n/locales/locale_el-GR.ini @@ -0,0 +1 @@ +hi = γεια, %s \ No newline at end of file diff --git a/_examples/intermediate/i18n/locales/locale_en-US.ini b/_examples/miscellaneous/i18n/locales/locale_en-US.ini similarity index 100% rename from _examples/intermediate/i18n/locales/locale_en-US.ini rename to _examples/miscellaneous/i18n/locales/locale_en-US.ini diff --git a/_examples/intermediate/i18n/locales/locale_zh-CN.ini b/_examples/miscellaneous/i18n/locales/locale_zh-CN.ini similarity index 100% rename from _examples/intermediate/i18n/locales/locale_zh-CN.ini rename to _examples/miscellaneous/i18n/locales/locale_zh-CN.ini diff --git a/_examples/intermediate/i18n/main.go b/_examples/miscellaneous/i18n/main.go similarity index 82% rename from _examples/intermediate/i18n/main.go rename to _examples/miscellaneous/i18n/main.go index 2bed8570..f56cf263 100644 --- a/_examples/intermediate/i18n/main.go +++ b/_examples/miscellaneous/i18n/main.go @@ -6,7 +6,7 @@ import ( "github.com/kataras/iris/middleware/i18n" ) -func main() { +func newApp() *iris.Application { app := iris.New() app.Use(i18n.New(i18n.Config{ @@ -27,23 +27,29 @@ func main() { // it tries to find the language by the "language" cookie // if didn't found then it it set to the Default setted on the configuration - // hi is the key, 'kataras' is the %s on the .ini file + // hi is the key, 'iris' is the %s on the .ini file // the second parameter is optional - // hi := ctx.Translate("hi", "kataras") + // hi := ctx.Translate("hi", "iris") // or: - hi := i18n.Translate(ctx, "hi", "kataras") + hi := i18n.Translate(ctx, "hi", "iris") - language := ctx.Values().GetString(ctx.Application().ConfigurationReadOnly().GetTranslateLanguageContextKey()) // return is form of 'en-US' + language := ctx.Values().GetString(ctx.Application().ConfigurationReadOnly().GetTranslateLanguageContextKey()) + // return is form of 'en-US' // The first succeed language found saved at the cookie with name ("language"), // you can change that by changing the value of the: iris.TranslateLanguageContextKey ctx.Writef("From the language %s translated output: %s", language, hi) }) + return app +} + +func main() { + app := newApp() + // go to http://localhost:8080/?lang=el-GR // or http://localhost:8080 // or http://localhost:8080/?lang=zh-CN app.Run(iris.Addr(":8080")) - } diff --git a/_examples/miscellaneous/i18n/main_test.go b/_examples/miscellaneous/i18n/main_test.go new file mode 100644 index 00000000..39d98cf8 --- /dev/null +++ b/_examples/miscellaneous/i18n/main_test.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "testing" + + "github.com/kataras/iris/httptest" +) + +func TestI18n(t *testing.T) { + app := newApp() + + expectedf := "From the language %s translated output: %s" + var ( + elgr = fmt.Sprintf(expectedf, "el-GR", "γεια, iris") + enus = fmt.Sprintf(expectedf, "en-US", "hello, iris") + zhcn = fmt.Sprintf(expectedf, "zh-CN", "您好,iris") + ) + + e := httptest.New(t, app) + // default is en-US + e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal(enus) + // default is en-US if lang query unable to be found + e.GET("/").WithQueryString("lang=un-EX").Expect().Status(httptest.StatusOK).Body().Equal(enus) + + e.GET("/").WithQueryString("lang=el-GR").Expect().Status(httptest.StatusOK).Body().Equal(elgr) + e.GET("/").WithQueryString("lang=en-US").Expect().Status(httptest.StatusOK).Body().Equal(enus) + e.GET("/").WithQueryString("lang=zh-CN").Expect().Status(httptest.StatusOK).Body().Equal(zhcn) +} diff --git a/_examples/beginner/pprof/main.go b/_examples/miscellaneous/pprof/main.go similarity index 100% rename from _examples/beginner/pprof/main.go rename to _examples/miscellaneous/pprof/main.go diff --git a/_examples/beginner/recover/main.go b/_examples/miscellaneous/recover/main.go similarity index 100% rename from _examples/beginner/recover/main.go rename to _examples/miscellaneous/recover/main.go diff --git a/_examples/beginner/overview/main.go b/_examples/overview/main.go similarity index 89% rename from _examples/beginner/overview/main.go rename to _examples/overview/main.go index aca8f688..298ddaa2 100644 --- a/_examples/beginner/overview/main.go +++ b/_examples/overview/main.go @@ -17,11 +17,11 @@ type User struct { func main() { app := iris.New() - + app.Logger().Level = iris.NoLog // 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.AttachView(view.HTML("./views", ".html").Reload(true)) + app.RegisterView(view.HTML("./views", ".html").Reload(true)) // Regster custom handler for specific http errors. app.OnErrorCode(iris.StatusInternalServerError, func(ctx context.Context) { @@ -36,12 +36,12 @@ func main() { }) app.Use(func(ctx context.Context) { - ctx.Application().Log("Begin request for path: %s", ctx.Path()) + ctx.Application().Logger().Infof("Begin request for path: %s", ctx.Path()) ctx.Next() }) - // app.Done(func(ctx context.Context) {]}) - + app.Subdomain("wtf.").Post("/decode", func(ctx context.Context) {}) + app.Subdomain("wtf.").Post("/decode", func(ctx context.Context) {}) // Method POST: http://localhost:8080/decode app.Post("/decode", func(ctx context.Context) { var user User @@ -78,7 +78,7 @@ func main() { } func logThisMiddleware(ctx context.Context) { - ctx.Application().Log("Path: %s | IP: %s", ctx.Path(), ctx.RemoteAddr()) + 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. diff --git a/_examples/beginner/overview/views/users/create_verification.html b/_examples/overview/views/users/create_verification.html similarity index 100% rename from _examples/beginner/overview/views/users/create_verification.html rename to _examples/overview/views/users/create_verification.html diff --git a/_examples/beginner/overview/views/users/profile.html b/_examples/overview/views/users/profile.html similarity index 100% rename from _examples/beginner/overview/views/users/profile.html rename to _examples/overview/views/users/profile.html diff --git a/_examples/beginner/routing/basic/main.go b/_examples/routing/basic/main.go similarity index 95% rename from _examples/beginner/routing/basic/main.go rename to _examples/routing/basic/main.go index 93fdea56..88ed1108 100644 --- a/_examples/beginner/routing/basic/main.go +++ b/_examples/routing/basic/main.go @@ -65,9 +65,9 @@ func main() { adminRoutes := app.Party("/admin", adminMiddleware) adminRoutes.Done(func(ctx context.Context) { // executes always last if ctx.Next() - ctx.Application().Log("response sent to " + ctx.Path()) + 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 intermediate/view examples. + // 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 context.Context) { @@ -154,9 +154,9 @@ func donateHandler(ctx context.Context) { func donateFinishHandler(ctx context.Context) { // values can be any type of object so we could cast the value to a string - // but Iris provides an easy to do that, if donate_url is not defined, then it returns an empty string instead. + // but iris provides an easy to do that, if donate_url is not defined, then it returns an empty string instead. donateURL := ctx.Values().GetString("donate_url") - ctx.Application().Log("donate_url value was: " + donateURL) + ctx.Application().Logger().Infof("donate_url value was: " + donateURL) ctx.Writef("\n\nDonate sent(?).") } diff --git a/_examples/intermediate/custom-context/method-overriding/main.go b/_examples/routing/custom-context/method-overriding/main.go similarity index 80% rename from _examples/intermediate/custom-context/method-overriding/main.go rename to _examples/routing/custom-context/method-overriding/main.go index b741f5c4..79ad5c7b 100644 --- a/_examples/intermediate/custom-context/method-overriding/main.go +++ b/_examples/routing/custom-context/method-overriding/main.go @@ -1,7 +1,7 @@ package main // In this package I'll show you how to override the existing Context's functions and methods. -// You can easly navigate to the advanced/custom-context to see how you can add new functions +// You can easly navigate to the custom-context example to see how you can add new functions // to your own context (need a custom handler). // // This way is far easier to understand and it's faster when you want to override existing methods: @@ -10,8 +10,6 @@ import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/sessions" - "github.com/kataras/iris/view" ) // Create your own custom Context, put any fields you wanna need. @@ -32,7 +30,7 @@ func (ctx *MyContext) Next() { // [...] func (ctx *MyContext) HTML(htmlContents string) (int, error) { - ctx.Application().Log("Executing .HTML function from MyContext") + ctx.Application().Logger().Infof("Executing .HTML function from MyContext") ctx.ContentType("text/html") return ctx.WriteString(htmlContents) @@ -41,14 +39,7 @@ func (ctx *MyContext) HTML(htmlContents string) (int, error) { func main() { app := iris.New() // Register a view engine on .html files inside the ./view/** directory. - viewEngine := view.HTML("./view", ".html") - app.AttachView(viewEngine) - - // Register the session manager. - sessionManager := sessions.New(sessions.Config{ - Cookie: "myappcookieid", - }) - app.AttachSessionManager(sessionManager) + app.RegisterView(iris.HTML("./view", ".html")) // The only one Required: // here is how you define how your own context will be created and acquired from the iris' generic context pool. @@ -81,6 +72,8 @@ func main() { // should always print "($PATH) Handler is executing from 'MyContext'" func recordWhichContextJustForProofOfConcept(ctx context.Context) { - ctx.Application().Log("(%s) Handler is executing from: '%s'", ctx.Path(), reflect.TypeOf(ctx).Elem().Name()) + ctx.Application().Logger().Infof("(%s) Handler is executing from: '%s'", ctx.Path(), reflect.TypeOf(ctx).Elem().Name()) ctx.Next() } + +// Look "new-implementation" to see how you can create an entirely new Context with new functions. diff --git a/_examples/intermediate/custom-context/method-overriding/view/hi.html b/_examples/routing/custom-context/method-overriding/view/hi.html similarity index 100% rename from _examples/intermediate/custom-context/method-overriding/view/hi.html rename to _examples/routing/custom-context/method-overriding/view/hi.html diff --git a/_examples/routing/custom-context/new-implementation/main.go b/_examples/routing/custom-context/new-implementation/main.go new file mode 100644 index 00000000..0b46c603 --- /dev/null +++ b/_examples/routing/custom-context/new-implementation/main.go @@ -0,0 +1,104 @@ +package main + +import ( + "sync" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/sessions" +) + +// Owner is our application structure, it contains the methods or fields we need, +// think it as the owner of our *Context. +type Owner struct { + // define here the fields that are global + // and shared to all clients. + sessionsManager *sessions.Sessions +} + +// this package-level variable "application" will be used inside context to communicate with our global Application. +var owner = &Owner{ + sessionsManager: sessions.New(sessions.Config{Cookie: "mysessioncookie"}), +} + +// Context is our custom context. +// Let's implement a context which will give us access +// to the client's Session with a trivial `ctx.Session()` call. +type Context struct { + context.Context + session *sessions.Session +} + +// Session returns the current client's session. +func (ctx *Context) Session() *sessions.Session { + // this help us if we call `Session()` multiple times in the same handler + if ctx.session == nil { + // start a new session if not created before. + ctx.session = owner.sessionsManager.Start(ctx.Context) + } + + return ctx.session +} + +// Bold will send a bold text to the client. +func (ctx *Context) Bold(text string) { + ctx.HTML("" + text + "") +} + +var contextPool = sync.Pool{New: func() interface{} { + return &Context{} +}} + +func acquire(original context.Context) *Context { + ctx := contextPool.Get().(*Context) + ctx.Context = original // set the context to the original one in order to have access to iris's implementation. + ctx.session = nil // reset the session + return ctx +} + +func release(ctx *Context) { + contextPool.Put(ctx) +} + +// Handler will convert our handler of func(*Context) to an iris Handler, +// in order to be compatible with the HTTP API. +func Handler(h func(*Context)) context.Handler { + return func(original context.Context) { + ctx := acquire(original) + h(ctx) + release(ctx) + } +} + +func newApp() *iris.Application { + app := iris.New() + + // Work as you did before, the only difference + // is that the original context.Handler should be wrapped with our custom + // `Handler` function. + app.Get("/", Handler(func(ctx *Context) { + ctx.Bold("Hello from our *Context") + })) + + app.Post("/set", Handler(func(ctx *Context) { + nameFieldValue := ctx.FormValue("name") + ctx.Session().Set("name", nameFieldValue) + ctx.Writef("set session = " + nameFieldValue) + })) + + app.Get("/get", Handler(func(ctx *Context) { + name := ctx.Session().GetString("name") + ctx.Writef(name) + })) + + return app +} + +func main() { + app := newApp() + + // GET: http://localhost:8080 + // POST: http://localhost:8080/set + // GET: http://localhost:8080/get + app.Run(iris.Addr(":8080")) +} diff --git a/_examples/routing/custom-context/new-implementation/main_test.go b/_examples/routing/custom-context/new-implementation/main_test.go new file mode 100644 index 00000000..35d8bb2c --- /dev/null +++ b/_examples/routing/custom-context/new-implementation/main_test.go @@ -0,0 +1,26 @@ +package main + +import ( + "testing" + + "github.com/kataras/iris/httptest" +) + +func TestCustomContextNewImpl(t *testing.T) { + app := newApp() + e := httptest.New(t, app, httptest.URL("http://localhost:8080")) + + e.GET("/").Expect(). + Status(httptest.StatusOK). + ContentType("text/html"). + Body().Equal("Hello from our *Context") + + expectedName := "iris" + e.POST("/set").WithFormField("name", expectedName).Expect(). + Status(httptest.StatusOK). + Body().Equal("set session = " + expectedName) + + e.GET("/get").Expect(). + Status(httptest.StatusOK). + Body().Equal(expectedName) +} diff --git a/_examples/beginner/routing/custom-wrapper/main.go b/_examples/routing/custom-wrapper/main.go similarity index 96% rename from _examples/beginner/routing/custom-wrapper/main.go rename to _examples/routing/custom-wrapper/main.go index a95b7b37..97dbe3b1 100644 --- a/_examples/beginner/routing/custom-wrapper/main.go +++ b/_examples/routing/custom-wrapper/main.go @@ -13,7 +13,7 @@ import ( // be executed in order to execute the registered routes' handlers. // // To see how you can serve files on root "/" without a custom wrapper -// just navigate to the "beginner/file-server/single-page-application" example. +// just navigate to the "file-server/single-page-application" example. // // This is just for the proof of concept, you can skip this tutorial if it's too much for you. func newApp() *iris.Application { @@ -78,7 +78,7 @@ func main() { // http://localhost:8080/index.html // http://localhost:8080/app.js // http://localhost:8080/css/main.css - // http://localhost:8080/profile/kataras + // http://localhost:8080/profile/anyusername app.Run(iris.Addr(":8080")) // Note: In this example we just saw one use case, diff --git a/_examples/beginner/routing/custom-wrapper/main_test.go b/_examples/routing/custom-wrapper/main_test.go similarity index 94% rename from _examples/beginner/routing/custom-wrapper/main_test.go rename to _examples/routing/custom-wrapper/main_test.go index e1dc1f84..74d36bdf 100644 --- a/_examples/beginner/routing/custom-wrapper/main_test.go +++ b/_examples/routing/custom-wrapper/main_test.go @@ -6,7 +6,6 @@ import ( "strings" "testing" - "github.com/kataras/iris" "github.com/kataras/iris/httptest" ) @@ -54,7 +53,7 @@ func TestCustomWrapper(t *testing.T) { contents := u.loadFromBase("./public") e.GET(url).Expect(). - Status(iris.StatusOK). + Status(httptest.StatusOK). Body().Equal(contents) } } diff --git a/_examples/beginner/routing/custom-wrapper/public/app.js b/_examples/routing/custom-wrapper/public/app.js similarity index 100% rename from _examples/beginner/routing/custom-wrapper/public/app.js rename to _examples/routing/custom-wrapper/public/app.js diff --git a/_examples/beginner/routing/custom-wrapper/public/css/main.css b/_examples/routing/custom-wrapper/public/css/main.css similarity index 100% rename from _examples/beginner/routing/custom-wrapper/public/css/main.css rename to _examples/routing/custom-wrapper/public/css/main.css diff --git a/_examples/beginner/routing/custom-wrapper/public/index.html b/_examples/routing/custom-wrapper/public/index.html similarity index 100% rename from _examples/beginner/routing/custom-wrapper/public/index.html rename to _examples/routing/custom-wrapper/public/index.html diff --git a/_examples/beginner/routing/dynamic-path/main.go b/_examples/routing/dynamic-path/main.go similarity index 95% rename from _examples/beginner/routing/dynamic-path/main.go rename to _examples/routing/dynamic-path/main.go index 43d44e0e..efc201c0 100644 --- a/_examples/beginner/routing/dynamic-path/main.go +++ b/_examples/routing/dynamic-path/main.go @@ -14,15 +14,15 @@ func main() { // 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 context.Context) + // iris, like net/http std package registers route's handlers + // by a Handler, the iris' type of handler is just a func(ctx context.Context) // where context comes from github.com/kataras/iris/context. // Until go 1.9 you will have to import that package too, after go 1.9 this will be not be necessary. // - // Iris has the easiest and the most powerful routing process you have ever meet. + // 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) + // iris has its own interpeter(yes like a programming language) // for route's path syntax and their dynamic path parameters parsing and evaluation, // I am calling them "macros" for shortcut. // How? It calculates its needs and if not any special regexp needed then it just @@ -74,7 +74,7 @@ func main() { // {param:int min(3)} // // - // Besides the fact that Iris provides the basic types and some default "macro funcs" + // 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: @@ -143,7 +143,7 @@ func main() { // let's use a trivial custom regexp that validates a single path parameter // which its value is only lowercase letters. - // http://localhost:8080/lowercase/kataras + // http://localhost:8080/lowercase/anylowercase app.Get("/lowercase/{name:string regexp(^[a-z]+)}", func(ctx context.Context) { ctx.Writef("name should be only lowercase, otherwise this handler will never executed: %s", ctx.Params().Get("name")) }) diff --git a/_examples/beginner/http-errors/main.go b/_examples/routing/http-errors/main.go similarity index 100% rename from _examples/beginner/http-errors/main.go rename to _examples/routing/http-errors/main.go diff --git a/_examples/beginner/routing/main.go b/_examples/routing/main.go similarity index 53% rename from _examples/beginner/routing/main.go rename to _examples/routing/main.go index c160a35e..31d3c4fc 100644 --- a/_examples/beginner/routing/main.go +++ b/_examples/routing/main.go @@ -12,27 +12,21 @@ Read: "overview" "basic" "dynamic-path" -and "reverse" examples if you want to release Iris' real power. +and "reverse" examples if you want to release iris' real power. */ const maxBodySize = 1 << 20 +const notFoundHTML = "

custom http error page

" -var app *iris.Application - -func init() { - app = iris.New() -} - -func registerErrors() { +func registerErrors(app *iris.Application) { // set a custom 404 handler app.OnErrorCode(iris.StatusNotFound, func(ctx context.Context) { - ctx.HTML("

custom http error page

") + ctx.HTML(notFoundHTML) }) } -func registerGamesRoutes() { +func registerGamesRoutes(app *iris.Application) { gamesMiddleware := func(ctx context.Context) { - println(ctx.Method() + ": " + ctx.Path()) ctx.Next() } @@ -64,19 +58,17 @@ func registerGamesRoutes() { } } -func registerSubdomains() { +func registerSubdomains(app *iris.Application) { mysubdomain := app.Party("mysubdomain.") // http://mysubdomain.myhost.com - mysubdomain.Get("/", func(ctx context.Context) { - ctx.Writef("Hello from subdomain: %s , from host: %s, method: %s and path: %s", ctx.Subdomain(), ctx.Host(), ctx.Method(), ctx.Path()) - }) + mysubdomain.Get("/", h) } -func main() { - registerErrors() - registerGamesRoutes() - registerSubdomains() - // more random examples below: +func newApp() *iris.Application { + app := iris.New() + registerErrors(app) + registerGamesRoutes(app) + registerSubdomains(app) app.Handle("GET", "/healthcheck", h) @@ -85,8 +77,7 @@ func main() { // and sends back the same body // remember, we have limit to that body in order // to protect ourselves from "over heating". - app.Post("/", func(ctx context.Context) { - ctx.SetMaxRequestBodySize(maxBodySize) // set max request body that client can send. + app.Post("/", context.LimitRequestBodySize(maxBodySize), func(ctx context.Context) { // get request body b, err := ioutil.ReadAll(ctx.Request().Body) // if is larger then send a bad request status @@ -99,10 +90,56 @@ func main() { ctx.Write(b) }) - // start the server on 0.0.0.0:8080 - app.Run(iris.Addr(":8080")) + return app } func h(ctx context.Context) { - ctx.HTML("

Path: " + ctx.Path() + "

") + 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() + + /* + // 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/games/42/players/93 + http://localhost:8080/games/42/clans/clan/93 + + // POST + http://localhost:8080/ + 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.Run(iris.Addr(":8080")) } diff --git a/_examples/routing/main_test.go b/_examples/routing/main_test.go new file mode 100644 index 00000000..1613cf7e --- /dev/null +++ b/_examples/routing/main_test.go @@ -0,0 +1,125 @@ +package main + +import ( + "strconv" + "strings" + "testing" + + "github.com/kataras/iris/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) + + var 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", "mysubdomain", "/", 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") +} diff --git a/_examples/beginner/routing/overview/main.go b/_examples/routing/overview/main.go similarity index 90% rename from _examples/beginner/routing/overview/main.go rename to _examples/routing/overview/main.go index 222bf998..5b901c1e 100644 --- a/_examples/beginner/routing/overview/main.go +++ b/_examples/routing/overview/main.go @@ -11,9 +11,9 @@ func main() { // GET: http://localhost:8080 app.Get("/", info) - // GET: http://localhost:8080/profile/kataras + // GET: http://localhost:8080/profile/anyusername app.Get("/profile/{username:string}", info) - // GET: http://localhost:8080/profile/kataras/backups/any/number/of/paths/here + // GET: http://localhost:8080/profile/anyusername/backups/any/number/of/paths/here app.Get("/profile/{username:string}/backups/{filepath:path}", info) // Favicon @@ -58,7 +58,7 @@ func main() { }) // GET: http://localhost:8080/users/42 - // **/users/42 and /users/help works after Iris version 7.0.5** + // **/users/42 and /users/help works after iris version 7.0.5** usersRoutes.Get("/{id:int}", func(ctx context.Context) { id, _ := ctx.Params().GetInt("id") ctx.Writef("get user by id: %d", id) @@ -85,7 +85,7 @@ func main() { // 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/intermediate/subdomains folder. + // See more subdomains examples at _examples/subdomains folder. adminRoutes := app.Party("admin.") // GET: http://admin.localhost:8080 @@ -100,8 +100,8 @@ func main() { dynamicSubdomainRoutes.Get("/", info) // GET: http://localhost:8080/ - // GET: http://localhost:8080/profile/kataras - // GET: http://localhost:8080/profile/kataras/backups/any/number/of/paths/here + // 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 diff --git a/_examples/beginner/routing/reverse/main.go b/_examples/routing/reverse/main.go similarity index 84% rename from _examples/beginner/routing/reverse/main.go rename to _examples/routing/reverse/main.go index d3dfe4ee..7cd888fc 100644 --- a/_examples/beginner/routing/reverse/main.go +++ b/_examples/routing/reverse/main.go @@ -12,12 +12,12 @@ func main() { // 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 context.Context) { + myroute := app.Get("/anything/{anythingparameter:path}", func(ctx context.Context) { paramValue := ctx.Params().Get("anythingparameter") ctx.Writef("The path after /anything is: %s", paramValue) }) - // useful for links, altough iris' view engine has the {{ urlpath "routename" "path values"}} already. + // useful for links, although iris' view engine has the {{ urlpath "routename" "path values"}} already. app.Get("/reverse_myroute", func(ctx context.Context) { myrouteRequestPath := rv.Path(myroute.Name, "any/path") ctx.HTML("Should be /anything/any/path: " + myrouteRequestPath) diff --git a/_examples/intermediate/route-state/main.go b/_examples/routing/route-state/main.go similarity index 78% rename from _examples/intermediate/route-state/main.go rename to _examples/routing/route-state/main.go index 300b6da6..847e2d41 100644 --- a/_examples/intermediate/route-state/main.go +++ b/_examples/routing/route-state/main.go @@ -8,7 +8,7 @@ import ( func main() { app := iris.New() - none, _ := app.None("/invisible/{username}", func(ctx context.Context) { + none := app.None("/invisible/{username}", func(ctx context.Context) { ctx.Writef("Hello %s with method: %s", ctx.Values().GetString("username"), ctx.Method()) if from := ctx.Values().GetString("from"); from != "" { @@ -29,10 +29,10 @@ func main() { }) app.Get("/execute", func(ctx context.Context) { - // same as navigating to "http://localhost:8080/invisible/kataras" when /change has being invoked and route state changed + // 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("GET", "/invisible/kataras") + ctx.Exec("GET", "/invisible/iris") }) app.Run(iris.Addr(":8080")) diff --git a/_examples/intermediate/subdomains/multi/hosts b/_examples/subdomains/multi/hosts similarity index 92% rename from _examples/intermediate/subdomains/multi/hosts rename to _examples/subdomains/multi/hosts index 6b40c422..b862e00e 100644 --- a/_examples/intermediate/subdomains/multi/hosts +++ b/_examples/subdomains/multi/hosts @@ -19,10 +19,10 @@ # 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 +#-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- +#-END iris- diff --git a/_examples/intermediate/subdomains/multi/main.go b/_examples/subdomains/multi/main.go similarity index 98% rename from _examples/intermediate/subdomains/multi/main.go rename to _examples/subdomains/multi/main.go index 11b1be56..4816b5bd 100644 --- a/_examples/intermediate/subdomains/multi/main.go +++ b/_examples/subdomains/multi/main.go @@ -36,6 +36,6 @@ func main() { // 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. + // that you can declare any syntactical correct name as a subdomain in iris. app.Run(iris.Addr("domain.local:80")) // for beginners: look ../hosts file } diff --git a/_examples/subdomains/multi/public/assets/images/test.ico b/_examples/subdomains/multi/public/assets/images/test.ico new file mode 100644 index 0000000000000000000000000000000000000000..961ef6dac80fa974d5bfeac74edcbf6797a7cd58 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x#lFaYI*xgi+L9|pukV-}_kLpIJv zQ+|#q=Ijh@h8zryI&2*6Mw}e2K=x7#W`=p@tPB&(xtKCdBpB*V1sJMK1Q|Na`B@rG z1(?I#%q()8)#sI4h=)em>M&S2JM(3F>bI4eiMKQva!V##sW7LXidT?ggs-)JM3aWP$dz37yar#qcC!5ioZpDxhah$9!u!Umsr=<*gHaFE6Qf{E}`U;Z>jK8QYs-^)A;&anZC&_Xo@Bod1;D zNS767cxq0`x4WHTDigV~%XjPI3cIfb2EwTf4BBjS>OD72%(L5UE5OI?>Fp%Fw8iuC z#C-doZK>Ax`}6D{*19Q8j*j+_s86suRAescR+;S)Jg3I(X{Ec$iF{|Z(2hLk(;05M zj){I24FCUcXP92&*>Pfy-~akBgSnMS4pXYU)K@uqxpJq6Sl1R=%c}gpW5AG;=;l`x zX>~HyO@B*zkY$4tFxAGXi!&6ZxH9A=x?0Syb9+?msums@?Bts1q2Fp??ZD*eW+xqL zqswZcYXD5qT8v&^j)v|ow(jSQB_ snFam>g^)3(is2;kALvyWmh1tR867~(f#$;mpcn{0U|{%(q#mRX0B!x$?f?J) literal 0 HcmV?d00001 diff --git a/_examples/subdomains/multi/public/upload_resources/favicon.ico b/_examples/subdomains/multi/public/upload_resources/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..961ef6dac80fa974d5bfeac74edcbf6797a7cd58 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x#lFaYI*xgi+L9|pukV-}_kLpIJv zQ+|#q=Ijh@h8zryI&2*6Mw}e2K=x7#W`=p@tPB&(xtKCdBpB*V1sJMK1Q|Na`B@rG z1(?I#%q()8)#sI4h=)em>M&S2JM(3F>bI4eiMKQva!V##sW7LXidT?ggs-)JM3aWP$dz37yar#qcC!5ioZpDxhah$9!u!Umsr=<*gHaFE6Qf{E}`U;Z>jK8QYs-^)A;&anZC&_Xo@Bod1;D zNS767cxq0`x4WHTDigV~%XjPI3cIfb2EwTf4BBjS>OD72%(L5UE5OI?>Fp%Fw8iuC z#C-doZK>Ax`}6D{*19Q8j*j+_s86suRAescR+;S)Jg3I(X{Ec$iF{|Z(2hLk(;05M zj){I24FCUcXP92&*>Pfy-~akBgSnMS4pXYU)K@uqxpJq6Sl1R=%c}gpW5AG;=;l`x zX>~HyO@B*zkY$4tFxAGXi!&6ZxH9A=x?0Syb9+?msums@?Bts1q2Fp??ZD*eW+xqL zqswZcYXD5qT8v&^j)v|ow(jSQB_ snFam>g^)3(is2;kALvyWmh1tR867~(f#$;mpcn{0U|{%(q#mRX0B!x$?f?J) literal 0 HcmV?d00001 diff --git a/_examples/intermediate/subdomains/single/hosts b/_examples/subdomains/single/hosts similarity index 92% rename from _examples/intermediate/subdomains/single/hosts rename to _examples/subdomains/single/hosts index 8e5c20d7..e7f394a1 100644 --- a/_examples/intermediate/subdomains/single/hosts +++ b/_examples/subdomains/single/hosts @@ -19,9 +19,9 @@ # 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 +#-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- +#-END iris- diff --git a/_examples/intermediate/subdomains/single/main.go b/_examples/subdomains/single/main.go similarity index 100% rename from _examples/intermediate/subdomains/single/main.go rename to _examples/subdomains/single/main.go diff --git a/_examples/intermediate/subdomains/wildcard/hosts b/_examples/subdomains/wildcard/hosts similarity index 93% rename from _examples/intermediate/subdomains/wildcard/hosts rename to _examples/subdomains/wildcard/hosts index 743ed62f..3b3b5664 100644 --- a/_examples/intermediate/subdomains/wildcard/hosts +++ b/_examples/subdomains/wildcard/hosts @@ -19,7 +19,7 @@ # 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 +#-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 @@ -27,4 +27,4 @@ 127.0.0.1 username4.mydomain.com 127.0.0.1 username5.mydomain.com -#-END IRIS- +#-END iris- diff --git a/_examples/intermediate/subdomains/wildcard/main.go b/_examples/subdomains/wildcard/main.go similarity index 100% rename from _examples/intermediate/subdomains/wildcard/main.go rename to _examples/subdomains/wildcard/main.go diff --git a/_examples/intermediate/subdomains/www/hosts b/_examples/subdomains/www/hosts similarity index 87% rename from _examples/intermediate/subdomains/www/hosts rename to _examples/subdomains/www/hosts index 2db79ded..ab304356 100644 --- a/_examples/intermediate/subdomains/www/hosts +++ b/_examples/subdomains/www/hosts @@ -19,7 +19,7 @@ # 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 iris-go.com -127.0.0.1 www.iris-go.com -#-END IRIS- +#-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/intermediate/subdomains/www/main.go b/_examples/subdomains/www/main.go similarity index 62% rename from _examples/intermediate/subdomains/www/main.go rename to _examples/subdomains/www/main.go index b455bf99..0bc8c4f4 100644 --- a/_examples/intermediate/subdomains/www/main.go +++ b/_examples/subdomains/www/main.go @@ -24,13 +24,11 @@ func newApp() *iris.Application { www := app.Party("www.") { - // get all routes that are registered so far, including all "Parties": + // get all routes that are registered so far, including all "Parties" but subdomains: currentRoutes := app.GetRoutes() // register them to the www subdomain/vhost as well: for _, r := range currentRoutes { - if _, err := www.Handle(r.Method, r.Path, r.Handlers...); err != nil { - app.Log("%s for www. failed: %v", r.Path, err) - } + www.Handle(r.Method, r.Path, r.Handlers...) } } @@ -39,18 +37,18 @@ func newApp() *iris.Application { func main() { app := newApp() - // http://iris-go.com - // http://iris-go.com/about - // http://iris-go.com/contact - // http://iris-go.com/api/users - // http://iris-go.com/api/users/42 + // http://mydomain.com + // http://mydomain.com/about + // http://imydomain.com/contact + // http://mydomain.com/api/users + // http://mydomain.com/api/users/42 - // http://www.iris-go.com - // http://www.iris-go.com/about - // http://www.iris-go.com/contact - // http://www.iris-go.com/api/users - // http://www.iris-go.com/api/users/42 - if err := app.Run(iris.Addr("iris-go.com:80")); err != nil { + // http://www.mydomain.com + // 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.Run(iris.Addr("mydomain.com:80")); err != nil { panic(err) } } diff --git a/_examples/intermediate/subdomains/www/main_test.go b/_examples/subdomains/www/main_test.go similarity index 95% rename from _examples/intermediate/subdomains/www/main_test.go rename to _examples/subdomains/www/main_test.go index 957ac946..f12c6bb8 100644 --- a/_examples/intermediate/subdomains/www/main_test.go +++ b/_examples/subdomains/www/main_test.go @@ -4,7 +4,6 @@ import ( "fmt" "testing" - "github.com/kataras/iris" "github.com/kataras/iris/httptest" ) @@ -52,7 +51,7 @@ func TestSubdomainWWW(t *testing.T) { } req.Expect(). - Status(iris.StatusOK). + Status(httptest.StatusOK). Body().Equal(test.response()) } diff --git a/_examples/intermediate/httptest/main.go b/_examples/testing/httptest/main.go similarity index 100% rename from _examples/intermediate/httptest/main.go rename to _examples/testing/httptest/main.go diff --git a/_examples/intermediate/httptest/main_test.go b/_examples/testing/httptest/main_test.go similarity index 55% rename from _examples/intermediate/httptest/main_test.go rename to _examples/testing/httptest/main_test.go index 3e4f73b1..492826b3 100644 --- a/_examples/intermediate/httptest/main_test.go +++ b/_examples/testing/httptest/main_test.go @@ -3,31 +3,29 @@ package main import ( "testing" - "github.com/kataras/iris" "github.com/kataras/iris/httptest" ) -// $ cd $GOPATH/src/github.com/kataras/iris/_examples/intermediate/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(iris.StatusUnauthorized) + e.GET("/").Expect().Status(httptest.StatusUnauthorized) // without basic auth - e.GET("/admin").Expect().Status(iris.StatusUnauthorized) + e.GET("/admin").Expect().Status(httptest.StatusUnauthorized) // with valid basic auth e.GET("/admin").WithBasicAuth("myusername", "mypassword").Expect(). - Status(iris.StatusOK).Body().Equal("/admin myusername:mypassword") + Status(httptest.StatusOK).Body().Equal("/admin myusername:mypassword") e.GET("/admin/profile").WithBasicAuth("myusername", "mypassword").Expect(). - Status(iris.StatusOK).Body().Equal("/admin/profile myusername:mypassword") + Status(httptest.StatusOK).Body().Equal("/admin/profile myusername:mypassword") e.GET("/admin/settings").WithBasicAuth("myusername", "mypassword").Expect(). - Status(iris.StatusOK).Body().Equal("/admin/settings myusername:mypassword") + Status(httptest.StatusOK).Body().Equal("/admin/settings myusername:mypassword") // with invalid basic auth e.GET("/admin/settings").WithBasicAuth("invalidusername", "invalidpassword"). - Expect().Status(iris.StatusUnauthorized) + Expect().Status(httptest.StatusUnauthorized) } diff --git a/_examples/advanced/online-visitors/main.go b/_examples/tutorial/online-visitors/main.go similarity index 88% rename from _examples/advanced/online-visitors/main.go rename to _examples/tutorial/online-visitors/main.go index 8206a0c7..f63026bd 100644 --- a/_examples/advanced/online-visitors/main.go +++ b/_examples/tutorial/online-visitors/main.go @@ -6,31 +6,24 @@ import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/view" "github.com/kataras/iris/websocket" ) -var ( - app *iris.Application - ws websocket.Server -) - -func init() { - // init the server instance - app = iris.New() - // load templaes - app.AttachView(view.HTML("./templates", ".html").Reload(true)) - // attach websocket server - ws = websocket.New(websocket.Config{Endpoint: "/my_endpoint"}) - ws.OnConnection(HandleWebsocketConnection) - ws.Attach(app) -} - -type page struct { - PageID string -} - func main() { + // init the web application instance + // app := iris.New() + app := iris.Default() + + // load templaes + app.RegisterView(iris.HTML("./templates", ".html").Reload(true)) + // setup the websocket server + ws := websocket.New(websocket.Config{}) + ws.OnConnection(HandleWebsocketConnection) + + app.Get("/my_endpoint", ws.Handler()) + app.Any("/iris-ws.js", websocket.ClientHandler()) + + // register static assets request path and system directory app.StaticWeb("/js", "./static/assets/js") h := func(ctx context.Context) { @@ -52,6 +45,10 @@ func main() { app.Run(iris.Addr(":8080")) } +type page struct { + PageID string +} + type pageView struct { source string count uint64 @@ -156,7 +153,7 @@ func HandleWebsocketConnection(c websocket.Connection) { // note: // we can also add a time.Sleep(2-3 seconds) inside the goroutine at the future if we don't need 'real-time' updates. go func(currentConnID string) { - for _, conn := range ws.GetConnectionsByRoom(roomName) { + for _, conn := range c.Server().GetConnectionsByRoom(roomName) { if conn.ID() != currentConnID { conn.Emit("watch", pageV.getCount()) } diff --git a/_examples/advanced/online-visitors/static/assets/js/visitors.js b/_examples/tutorial/online-visitors/static/assets/js/visitors.js similarity index 100% rename from _examples/advanced/online-visitors/static/assets/js/visitors.js rename to _examples/tutorial/online-visitors/static/assets/js/visitors.js diff --git a/_examples/advanced/online-visitors/templates/index.html b/_examples/tutorial/online-visitors/templates/index.html similarity index 100% rename from _examples/advanced/online-visitors/templates/index.html rename to _examples/tutorial/online-visitors/templates/index.html diff --git a/_examples/advanced/online-visitors/templates/other.html b/_examples/tutorial/online-visitors/templates/other.html similarity index 100% rename from _examples/advanced/online-visitors/templates/other.html rename to _examples/tutorial/online-visitors/templates/other.html diff --git a/_examples/advanced/url-shortener/main.go b/_examples/tutorial/url-shortener/main.go similarity index 65% rename from _examples/advanced/url-shortener/main.go rename to _examples/tutorial/url-shortener/main.go index 654b4538..2e247620 100644 --- a/_examples/advanced/url-shortener/main.go +++ b/_examples/tutorial/url-shortener/main.go @@ -1,4 +1,4 @@ -// Package main shows how you can create a simple URL SHortener using only Iris and BoltDB. +// Package main shows how you can create a simple URL SHortener. // // $ go get github.com/boltdb/bolt/... // $ go run main.go @@ -8,45 +8,58 @@ package main import ( "bytes" "html/template" - "math/rand" "net/url" - "time" - - "github.com/boltdb/bolt" "github.com/kataras/iris" "github.com/kataras/iris/context" "github.com/kataras/iris/view" + + "github.com/boltdb/bolt" + + "github.com/satori/go.uuid" ) func main() { - app := iris.New() - - // assign a variable to the DB so we can use its features later + // 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.Run(iris.Addr(":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 := view.HTML("./templates", ".html").Reload(true) - // template funcs + // register any template func(s) here. // - // look ./templates/index.html#L16 - tmpl.AddFunc("isPositive", func(n int) bool { + // Look ./templates/index.html#L16 + tmpl.AddFunc("IsPositive", func(n int) bool { if n > 0 { return true } return false }) - app.AttachView(tmpl) + app.RegisterView(tmpl) // Serve static files (css) app.StaticWeb("/static", "./resources") - app.Get("/", func(ctx context.Context) { - ctx.ViewData("url_count", db.Len()) + indexHandler := func(ctx context.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 @@ -65,38 +78,44 @@ func main() { ctx.Redirect(value, iris.StatusTemporaryRedirect) } - app.Get("/u/:shortkey", func(ctx context.Context) { + app.Get("/u/{shortkey}", func(ctx context.Context) { execShortURL(ctx, ctx.Params().Get("shortkey")) }) app.Post("/shorten", func(ctx context.Context) { formValue := ctx.FormValue("url") if formValue == "" { - ctx.ViewData("form_result", "You need to a enter a URL") + 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.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.Log("while saving URL: " + err.Error()) + 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", + ctx.ViewData("FORM_RESULT", template.HTML("
"+shortenURL+" 
")) } } } - ctx.ViewData("url_count", db.Len()) - ctx.View("index.html") + + indexHandler(ctx) // no redirect, we need the FORM_RESULT. }) - app.Run(iris.Addr(":8080")) + app.Post("/clear_cache", func(ctx context.Context) { + db.Clear() + ctx.Redirect("/") + }) - db.Close() + return app } // +------------------------------------------------------------+ @@ -116,7 +135,7 @@ type Store interface { Set(key string, value string) error // error if something went wrong Get(key string) string // empty value if not found Len() int // should return the number of all the records/tables/buckets - Close() error // release the store or ignore + Close() // release the store or ignore } var ( @@ -172,28 +191,57 @@ func NewDB(stumb string) *DB { // Set sets a shorten url and its key // Note: Caller is responsible to generate a key. func (d *DB) Set(key string, value string) error { - d.db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket(tableURLs) + return d.db.Update(func(tx *bolt.Tx) error { + b, err := tx.CreateBucketIfNotExists(tableURLs) // Generate ID for the url // Note: we could use that instead of a random string key // but we want to simulate a real-world url shortener // so we skip that. // id, _ := b.NextSequence() - return b.Put([]byte(key), []byte(value)) + if err != nil { + return err + } + + k := []byte(key) + valueB := []byte(value) + c := b.Cursor() + + found := false + for k, v := c.First(); k != nil; k, v = c.Next() { + if bytes.Equal(valueB, v) { + found = true + break + } + } + // if value already exists don't re-put it. + if found { + return nil + } + + return b.Put(k, []byte(value)) + }) +} + +// Clear clears all the database entries for the table urls. +func (d *DB) Clear() error { + return d.db.Update(func(tx *bolt.Tx) error { + return tx.DeleteBucket(tableURLs) }) - return nil } // Get returns a url by its key. // // Returns an empty string if not found. func (d *DB) Get(key string) (value string) { - keyb := []byte(key) - d.db.View(func(tx *bolt.Tx) error { + keyB := []byte(key) + d.db.Update(func(tx *bolt.Tx) error { b := tx.Bucket(tableURLs) + if b == nil { + return nil + } c := b.Cursor() for k, v := c.First(); k != nil; k, v = c.Next() { - if bytes.Equal(keyb, k) { + if bytes.Equal(keyB, k) { value = string(v) break } @@ -205,12 +253,37 @@ func (d *DB) Get(key string) (value string) { return } +// GetByValue returns all keys for a specific (original) url value. +func (d *DB) GetByValue(value string) (keys []string) { + valueB := []byte(value) + d.db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket(tableURLs) + if b == nil { + return nil + } + c := b.Cursor() + // first for the bucket's table "urls" + for k, v := c.First(); k != nil; k, v = c.Next() { + if bytes.Equal(valueB, v) { + keys = append(keys, string(k)) + } + } + + return nil + }) + + return +} + // Len returns all the "shorted" urls length func (d *DB) Len() (num int) { d.db.View(func(tx *bolt.Tx) error { // Assume bucket exists and has keys b := tx.Bucket(tableURLs) + if b == nil { + return nil + } b.ForEach(func([]byte, []byte) error { num++ @@ -221,9 +294,11 @@ func (d *DB) Len() (num int) { return } -// Close the data(base) connection -func (d *DB) Close() error { - return d.db.Close() +// Close shutdowns the data(base) connection. +func (d *DB) Close() { + if err := d.db.Close(); err != nil { + Panic(err) + } } // +------------------------------------------------------------+ @@ -232,11 +307,13 @@ func (d *DB) Close() error { // | | // +------------------------------------------------------------+ -// Generator the type to generate keys(short urls) based on 'n' -type Generator func(n int) string +// Generator the type to generate keys(short urls) +type Generator func() string -// DefaultGenerator is the defautl url generator (the simple randomString) -var DefaultGenerator = randomString +// DefaultGenerator is the defautl url generator +var DefaultGenerator = func() string { + return uuid.NewV4().String() +} // Factory is responsible to generate keys(short urls) type Factory struct { @@ -261,40 +338,15 @@ func (f *Factory) Gen(uri string) (key string, err error) { if err != nil { return "", err } - key = f.generator(len(uri)) + + key = f.generator() // Make sure that the key is unique for { if v := f.store.Get(key); v == "" { break } - key = f.generator((len(uri) / 2) + 1) + key = f.generator() } return key, nil } - -const ( - letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - 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 string(b) -} diff --git a/_examples/tutorial/url-shortener/main_test.go b/_examples/tutorial/url-shortener/main_test.go new file mode 100644 index 00000000..b6f0216e --- /dev/null +++ b/_examples/tutorial/url-shortener/main_test.go @@ -0,0 +1,74 @@ +package main + +import ( + "io/ioutil" + "os" + "time" + + "testing" + + "github.com/kataras/iris/httptest" +) + +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

-

{{ .form_result}}

+

{{ .FORM_RESULT}}

- {{ if isPositive .url_count }} -

{{ .url_count }} URLs shortened

+ {{ if IsPositive .URL_COUNT }} +

{{ .URL_COUNT }} URLs shortened

{{ end }} + +
+ +
\ No newline at end of file diff --git a/_examples/intermediate/view/context-view-data/main.go b/_examples/view/context-view-data/main.go similarity index 77% rename from _examples/intermediate/view/context-view-data/main.go rename to _examples/view/context-view-data/main.go index ad148292..3e008be7 100644 --- a/_examples/intermediate/view/context-view-data/main.go +++ b/_examples/view/context-view-data/main.go @@ -1,7 +1,3 @@ -// this example will show you how you can set per-request data for a template outside of the main handler which calls -// the .Render, via middleware. -// -// Remember: .Render has the "binding" argument which can be used to send data to the template at any case. package main import ( @@ -9,7 +5,6 @@ import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/view" ) const ( @@ -22,7 +17,7 @@ func main() { // output startup banner and error logs on os.Stdout // set the view engine target to ./templates folder - app.AttachView(view.HTML("./templates", ".html").Reload(true)) + app.RegisterView(iris.HTML("./templates", ".html").Reload(true)) app.Use(func(ctx context.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 @@ -37,7 +32,7 @@ func main() { app.Get("/", func(ctx context.Context) { ctx.ViewData("BodyMessage", "a sample text here... setted by the route handler") if err := ctx.View("index.html"); err != nil { - ctx.Application().Log(err.Error()) + ctx.Application().Logger().Infof(err.Error()) } }) @@ -47,7 +42,7 @@ func main() { // same file, just to keep things simple. if err := ctx.View("index.html"); err != nil { - ctx.Application().Log(err.Error()) + ctx.Application().Logger().Infof(err.Error()) } }) diff --git a/_examples/intermediate/view/context-view-data/templates/index.html b/_examples/view/context-view-data/templates/index.html similarity index 100% rename from _examples/intermediate/view/context-view-data/templates/index.html rename to _examples/view/context-view-data/templates/index.html diff --git a/_examples/intermediate/view/context-view-data/templates/layouts/layout.html b/_examples/view/context-view-data/templates/layouts/layout.html similarity index 100% rename from _examples/intermediate/view/context-view-data/templates/layouts/layout.html rename to _examples/view/context-view-data/templates/layouts/layout.html diff --git a/_examples/intermediate/view/embedding-templates-into-app/bindata.go b/_examples/view/embedding-templates-into-app/bindata.go similarity index 100% rename from _examples/intermediate/view/embedding-templates-into-app/bindata.go rename to _examples/view/embedding-templates-into-app/bindata.go diff --git a/_examples/intermediate/view/embedding-templates-into-app/main.go b/_examples/view/embedding-templates-into-app/main.go similarity index 83% rename from _examples/intermediate/view/embedding-templates-into-app/main.go rename to _examples/view/embedding-templates-into-app/main.go index 5a01ace9..8310e818 100644 --- a/_examples/intermediate/view/embedding-templates-into-app/main.go +++ b/_examples/view/embedding-templates-into-app/main.go @@ -3,7 +3,6 @@ package main import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/view" ) func main() { @@ -13,7 +12,7 @@ func main() { // $ go build // $ ./embedding-templates-into-app // html files are not used, you can delete the folder and run the example - app.AttachView(view.HTML("./templates", ".html").Binary(Asset, AssetNames)) + app.RegisterView(iris.HTML("./templates", ".html").Binary(Asset, AssetNames)) app.Get("/", hi) // http://localhost:8080 diff --git a/_examples/intermediate/view/embedding-templates-into-app/templates/hi.html b/_examples/view/embedding-templates-into-app/templates/hi.html similarity index 100% rename from _examples/intermediate/view/embedding-templates-into-app/templates/hi.html rename to _examples/view/embedding-templates-into-app/templates/hi.html diff --git a/_examples/view/overview/main.go b/_examples/view/overview/main.go new file mode 100644 index 00000000..af554bea --- /dev/null +++ b/_examples/view/overview/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +func main() { + app := iris.New() + + // - standard html | iris.HTML(...) + // - django | iris.Django(...) + // - pug(jade) | iris.Pug(...) + // - handlebars | iris.Handlebars(...) + // - amber | iris.Amber(...) + // 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 context.Context) { + + ctx.ViewData("Name", "iris") // the .Name inside the ./templates/hi.html + ctx.Gzip(true) // enable gzip for big files + ctx.View("hi.html") // render the template with the file name relative to the './templates' + + }) + + // http://localhost:8080/ + app.Run(iris.Addr(":8080")) +} + +/* +Note: + +In case you're wondering, the code behind the view engines derives from the "github.com/kataras/iris/view" package, +access to the engines' variables can be granded by "github.com/kataras/iris" 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/intermediate/view/overview/templates/hi.html b/_examples/view/overview/templates/hi.html similarity index 64% rename from _examples/intermediate/view/overview/templates/hi.html rename to _examples/view/overview/templates/hi.html index bb7e3e71..1b857add 100644 --- a/_examples/intermediate/view/overview/templates/hi.html +++ b/_examples/view/overview/templates/hi.html @@ -1,8 +1,11 @@ + -Hi Iris + Hi iris +

Hi {{.Name}}

- + + \ No newline at end of file diff --git a/_examples/intermediate/view/template_html_0/main.go b/_examples/view/template_html_0/main.go similarity index 50% rename from _examples/intermediate/view/template_html_0/main.go rename to _examples/view/template_html_0/main.go index b55e2c56..5b76c64e 100644 --- a/_examples/intermediate/view/template_html_0/main.go +++ b/_examples/view/template_html_0/main.go @@ -3,19 +3,18 @@ package main import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/view" ) func main() { app := iris.New() // defaults to these - // - standard html | view.HTML(...) - // - django | view.Django(...) - // - pug(jade) | view.Pug(...) - // - handlebars | view.Handlebars(...) - // - amber | view.Amber(...) + // - standard html | iris.HTML(...) + // - django | iris.Django(...) + // - pug(jade) | iris.Pug(...) + // - handlebars | iris.Handlebars(...) + // - amber | iris.Amber(...) - tmpl := view.HTML("./templates", ".html") + tmpl := iris.HTML("./templates", ".html") tmpl.Reload(true) // reload templates on each request (development mode) // default template funcs are: // @@ -27,7 +26,7 @@ func main() { tmpl.AddFunc("greet", func(s string) string { return "Greetings " + s + "!" }) - app.AttachView(tmpl) + app.RegisterView(tmpl) app.Get("/", hi) @@ -37,7 +36,20 @@ func main() { func hi(ctx context.Context) { ctx.ViewData("Title", "Hi Page") - ctx.ViewData("Name", "Iris") // {{.Name}} will render: Iris + 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/view" package, +access to the engines' variables can be granded by "github.com/kataras/iris" 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/intermediate/view/template_html_0/templates/hi.html b/_examples/view/template_html_0/templates/hi.html similarity index 100% rename from _examples/intermediate/view/template_html_0/templates/hi.html rename to _examples/view/template_html_0/templates/hi.html diff --git a/_examples/intermediate/view/template_html_1/main.go b/_examples/view/template_html_1/main.go similarity index 86% rename from _examples/intermediate/view/template_html_1/main.go rename to _examples/view/template_html_1/main.go index c78076c8..a4c32c8f 100644 --- a/_examples/intermediate/view/template_html_1/main.go +++ b/_examples/view/template_html_1/main.go @@ -3,7 +3,6 @@ package main import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/view" ) type mypage struct { @@ -14,7 +13,7 @@ type mypage struct { func main() { app := iris.New() - app.AttachView(view.HTML("./templates", ".html").Layout("layout.html")) + app.RegisterView(iris.HTML("./templates", ".html").Layout("layout.html")) // TIP: append .Reload(true) to reload the templates on each request. app.Get("/", func(ctx context.Context) { diff --git a/_examples/intermediate/view/template_html_1/templates/layout.html b/_examples/view/template_html_1/templates/layout.html similarity index 100% rename from _examples/intermediate/view/template_html_1/templates/layout.html rename to _examples/view/template_html_1/templates/layout.html diff --git a/_examples/intermediate/view/template_html_1/templates/mypage.html b/_examples/view/template_html_1/templates/mypage.html similarity index 100% rename from _examples/intermediate/view/template_html_1/templates/mypage.html rename to _examples/view/template_html_1/templates/mypage.html diff --git a/_examples/intermediate/view/template_html_2/README.md b/_examples/view/template_html_2/README.md similarity index 100% rename from _examples/intermediate/view/template_html_2/README.md rename to _examples/view/template_html_2/README.md diff --git a/_examples/intermediate/view/template_html_2/main.go b/_examples/view/template_html_2/main.go similarity index 90% rename from _examples/intermediate/view/template_html_2/main.go rename to _examples/view/template_html_2/main.go index 5acfd0c7..46152c30 100644 --- a/_examples/intermediate/view/template_html_2/main.go +++ b/_examples/view/template_html_2/main.go @@ -3,19 +3,18 @@ package main import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/view" ) func main() { app := iris.New() - tmpl := view.HTML("./templates", ".html") + tmpl := iris.HTML("./templates", ".html") tmpl.Layout("layouts/layout.html") tmpl.AddFunc("greet", func(s string) string { return "Greetings " + s + "!" }) - app.AttachView(tmpl) + app.RegisterView(tmpl) app.Get("/", func(ctx context.Context) { if err := ctx.View("page1.html"); err != nil { @@ -26,7 +25,7 @@ func main() { // remove the layout for a specific route app.Get("/nolayout", func(ctx context.Context) { - ctx.ViewLayout(view.NoLayout) + ctx.ViewLayout(iris.NoLayout) if err := ctx.View("page1.html"); err != nil { ctx.StatusCode(iris.StatusInternalServerError) ctx.Writef(err.Error()) diff --git a/_examples/intermediate/view/template_html_2/templates/layouts/layout.html b/_examples/view/template_html_2/templates/layouts/layout.html similarity index 100% rename from _examples/intermediate/view/template_html_2/templates/layouts/layout.html rename to _examples/view/template_html_2/templates/layouts/layout.html diff --git a/_examples/intermediate/view/template_html_2/templates/layouts/mylayout.html b/_examples/view/template_html_2/templates/layouts/mylayout.html similarity index 100% rename from _examples/intermediate/view/template_html_2/templates/layouts/mylayout.html rename to _examples/view/template_html_2/templates/layouts/mylayout.html diff --git a/_examples/intermediate/view/template_html_2/templates/page1.html b/_examples/view/template_html_2/templates/page1.html similarity index 100% rename from _examples/intermediate/view/template_html_2/templates/page1.html rename to _examples/view/template_html_2/templates/page1.html diff --git a/_examples/intermediate/view/template_html_2/templates/partials/page1_partial1.html b/_examples/view/template_html_2/templates/partials/page1_partial1.html similarity index 100% rename from _examples/intermediate/view/template_html_2/templates/partials/page1_partial1.html rename to _examples/view/template_html_2/templates/partials/page1_partial1.html diff --git a/_examples/intermediate/view/template_html_3/main.go b/_examples/view/template_html_3/main.go similarity index 63% rename from _examples/intermediate/view/template_html_3/main.go rename to _examples/view/template_html_3/main.go index 9b5a4214..650ddb87 100644 --- a/_examples/intermediate/view/template_html_3/main.go +++ b/_examples/view/template_html_3/main.go @@ -4,37 +4,30 @@ package main import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/view" ) func main() { app := iris.New() - if err := app.AttachView(view.HTML("./templates", ".html").Reload(true)); err != nil { - panic(err) - } + app.RegisterView(iris.HTML("./templates", ".html").Reload(true)) - mypathRoute, _ := app.Get("/mypath", writePathHandler) + mypathRoute := app.Get("/mypath", writePathHandler) mypathRoute.Name = "my-page1" - mypath2Route, err := app.Get("/mypath2/{paramfirst}/{paramsecond}", writePathHandler) - // same as: app.Get("/mypath2/:paramfirst/:paramsecond", writePathHandler) - if err != nil { // catch errors when creating a route or catch them on err := .Run, it's up to you. - panic(err) - } + mypath2Route := app.Get("/mypath2/{paramfirst}/{paramsecond}", writePathHandler) mypath2Route.Name = "my-page2" - mypath3Route, _ := app.Get("/mypath3/{paramfirst}/statichere/{paramsecond}", writePathHandler) + mypath3Route := app.Get("/mypath3/{paramfirst}/statichere/{paramsecond}", writePathHandler) mypath3Route.Name = "my-page3" - mypath4Route, _ := app.Get("/mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}", writePathHandler) + 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 := 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 := app.Get("/mypath6/{paramfirst}/{paramsecond}/statichere/{paramThirdAfterStatic}", writePathHandler) mypath6Route.Name = "my-page6" app.Get("/", func(ctx context.Context) { diff --git a/_examples/intermediate/view/template_html_3/templates/page.html b/_examples/view/template_html_3/templates/page.html similarity index 100% rename from _examples/intermediate/view/template_html_3/templates/page.html rename to _examples/view/template_html_3/templates/page.html diff --git a/_examples/intermediate/view/template_html_4/hosts b/_examples/view/template_html_4/hosts similarity index 92% rename from _examples/intermediate/view/template_html_4/hosts rename to _examples/view/template_html_4/hosts index ecc92ad3..5b14cf63 100644 --- a/_examples/intermediate/view/template_html_4/hosts +++ b/_examples/view/template_html_4/hosts @@ -19,7 +19,7 @@ # 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 +#-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 @@ -27,6 +27,6 @@ 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- +#-END iris- # Windows: Drive:/Windows/system32/drivers/etc/hosts, on Linux: /etc/hosts \ No newline at end of file diff --git a/_examples/intermediate/view/template_html_4/main.go b/_examples/view/template_html_4/main.go similarity index 68% rename from _examples/intermediate/view/template_html_4/main.go rename to _examples/view/template_html_4/main.go index bbf5c52a..495d94e8 100644 --- a/_examples/intermediate/view/template_html_4/main.go +++ b/_examples/view/template_html_4/main.go @@ -5,7 +5,6 @@ import ( "github.com/kataras/iris" "github.com/kataras/iris/context" "github.com/kataras/iris/core/router" - "github.com/kataras/iris/view" ) const ( @@ -19,37 +18,34 @@ func main() { // 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 := view.HTML("./templates", ".html") + 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.AttachView(templates) + 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 := subdomain.Get("/mypath", emptyHandler) mypathRoute.Name = "my-page1" - mypath2Route, _ := subdomain.Get("/mypath2/{paramfirst}/{paramsecond}", emptyHandler) + mypath2Route := subdomain.Get("/mypath2/{paramfirst}/{paramsecond}", emptyHandler) mypath2Route.Name = "my-page2" - mypath3Route, _ := subdomain.Get("/mypath3/{paramfirst}/statichere/{paramsecond}", emptyHandler) + mypath3Route := subdomain.Get("/mypath3/{paramfirst}/statichere/{paramsecond}", emptyHandler) mypath3Route.Name = "my-page3" - mypath4Route, _ := subdomain.Get("/mypath4/{paramfirst}/statichere/{paramsecond}/{otherparam}/{something:path}", emptyHandler) + 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 := subdomain.Handle("GET", "/mypath5/{paramfirst}/statichere/{paramsecond}/{otherparam}/anything/{something:path}", emptyHandler) mypath5Route.Name = "my-page5" - mypath6Route, err := subdomain.Get("/mypath6/{paramfirst}/{paramsecond}/staticParam/{paramThirdAfterStatic}", emptyHandler) - if err != nil { // catch any route problems when declare a route or on err := app.Run(...); err != nil { panic(err) } - panic(err) - } + mypath6Route := subdomain.Get("/mypath6/{paramfirst}/{paramsecond}/staticParam/{paramThirdAfterStatic}", emptyHandler) mypath6Route.Name = "my-page6" app.Get("/", func(ctx context.Context) { diff --git a/_examples/intermediate/view/template_html_4/templates/page.html b/_examples/view/template_html_4/templates/page.html similarity index 100% rename from _examples/intermediate/view/template_html_4/templates/page.html rename to _examples/view/template_html_4/templates/page.html diff --git a/cache/AUTHORS b/cache/AUTHORS new file mode 100644 index 00000000..f60e7cea --- /dev/null +++ b/cache/AUTHORS @@ -0,0 +1,5 @@ +# This is the official list of Iris Cache authors for copyright +# purposes. + +Gerasimos Maropoulos +Bill Qeras, Jr. \ No newline at end of file diff --git a/cache/LICENSE b/cache/LICENSE index 7eb6b6e4..7cd7c2ce 100644 --- a/cache/LICENSE +++ b/cache/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. +Copyright (c) 2017 The Iris Cache Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,8 +10,8 @@ notice, this list of conditions and the following disclaimer. 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 Gerasimos Maropoulos nor the name of his -username, kataras, may be used to endorse or promote products derived from + * 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 diff --git a/cache/README.md b/cache/README.md new file mode 100644 index 00000000..5637d4f6 --- /dev/null +++ b/cache/README.md @@ -0,0 +1,7 @@ +# Cache + +Fast HTTP Cache support for the [iris](https://github.com/kataras/iris) web framework. + +## Table of contents + +* [Simple](_examples/simple/main.go) \ No newline at end of file diff --git a/_examples/intermediate/cache-markdown/main.go b/cache/_examples/simple/main.go similarity index 96% rename from _examples/intermediate/cache-markdown/main.go rename to cache/_examples/simple/main.go index dcb24cce..3a86df78 100644 --- a/_examples/intermediate/cache-markdown/main.go +++ b/cache/_examples/simple/main.go @@ -5,6 +5,8 @@ import ( "github.com/kataras/iris" "github.com/kataras/iris/context" + + "github.com/kataras/iris/cache" ) var markdownContents = []byte(`## Hello Markdown @@ -58,7 +60,7 @@ All features of Sundown are supported, including: func main() { app := iris.New() - app.Get("/", iris.Cache(10*time.Second), writeMarkdown) + 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 resetted. diff --git a/cache/cache.go b/cache/cache.go index 5667e72c..1f129b4a 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - /* Package cache provides cache capabilities with rich support of options and rules. Example code: @@ -11,8 +7,8 @@ Example code: "time" "github.com/kataras/iris" - "github.com/kataras/iris/cache" "github.com/kataras/iris/context" + "github.com/kataras/iris/cache" ) func main(){ @@ -42,7 +38,7 @@ import ( // if the expiration <=2 seconds then expiration is taken by the "cache-control's maxage" header // returns context.Handler, which you can use as your default router or per-route handler // -// All type of responses are cached, templates, json, text, anything. +// All types of response can be cached, templates, json, text, anything. // // You can add validators with this function. func Cache(bodyHandler context.Handler, expiration time.Duration) *client.Handler { @@ -55,9 +51,9 @@ func Cache(bodyHandler context.Handler, expiration time.Duration) *client.Handle // if the expiration <=2 seconds then expiration is taken by the "cache-control's maxage" header // returns context.Handler, which you can use as your default router or per-route handler // -// All type of responses are cached, templates, json, text, anything. +// All types of response can be cached, templates, json, text, anything. // -// it returns a context.Handler, for more options use the .Cache . +// it returns a context.Handler, for more options use the `Cache` func WrapHandler(bodyHandler context.Handler, expiration time.Duration) context.Handler { return Cache(bodyHandler, expiration).ServeHTTP } @@ -69,17 +65,18 @@ func WrapHandler(bodyHandler context.Handler, expiration time.Duration) context. // // It's the same as Cache and WrapHandler but it sets the "bodyHandler" to the next handler in the chain. // -// All type of responses are cached, templates, json, text, anything. +// All types of response can be cached, templates, json, text, anything. // -// it returns a context.Handler, for more options use the .Cache . +// it returns a context.Handler which can be used as a middleware, for more options use the `Cache`. +// +// Examples can be found at: https://github.com/kataras/iris/tree/master/_examples/#caching func Handler(expiration time.Duration) context.Handler { h := WrapHandler(nil, expiration) return h } var ( - // NoCache called when a particular handler is not valid for cache. - // If this function called inside a handler then the handler is not cached - // even if it's surrounded with the Cache/CacheFunc/CacheRemote wrappers. + // NoCache disables the cache for a particular request, + // can be used as a middleware or called manually from the handler. NoCache = client.NoCache ) diff --git a/cache/cache_test.go b/cache/cache_test.go index b8dbaa0c..12d448c6 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -1,4 +1,3 @@ -// black-box testing package cache_test import ( diff --git a/cache/cfg/cfg.go b/cache/cfg/cfg.go index 645e01c8..dbd20f3e 100644 --- a/cache/cfg/cfg.go +++ b/cache/cfg/cfg.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package cfg import "time" diff --git a/cache/client/client.go b/cache/client/client.go index d127f672..d8e83522 100644 --- a/cache/client/client.go +++ b/cache/client/client.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package client import ( diff --git a/cache/client/handler.go b/cache/client/handler.go index d59ae574..6438bbb3 100644 --- a/cache/client/handler.go +++ b/cache/client/handler.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package client import ( @@ -31,11 +27,12 @@ type Handler struct { entry *entry.Entry } -// NewHandler returns a new cached handler +// NewHandler returns a new cached handler for the "bodyHandler" +// which expires every "expiration". func NewHandler(bodyHandler context.Handler, - expireDuration time.Duration) *Handler { + expiration time.Duration) *Handler { - e := entry.NewEntry(expireDuration) + e := entry.NewEntry(expiration) return &Handler{ bodyHandler: bodyHandler, diff --git a/cache/client/response_recorder.go b/cache/client/response_recorder.go index 2b4cd670..04dd1a7d 100644 --- a/cache/client/response_recorder.go +++ b/cache/client/response_recorder.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package client import ( diff --git a/cache/client/rule/chained.go b/cache/client/rule/chained.go index 166121fe..fc4b59e3 100644 --- a/cache/client/rule/chained.go +++ b/cache/client/rule/chained.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package rule import ( diff --git a/cache/client/rule/conditional.go b/cache/client/rule/conditional.go index 7ade58d6..e4adc630 100644 --- a/cache/client/rule/conditional.go +++ b/cache/client/rule/conditional.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package rule import ( diff --git a/cache/client/rule/header.go b/cache/client/rule/header.go index edcc3b5b..33d65553 100644 --- a/cache/client/rule/header.go +++ b/cache/client/rule/header.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package rule import ( diff --git a/cache/client/rule/not_satisfied.go b/cache/client/rule/not_satisfied.go index 0d14a429..4a6e9d58 100644 --- a/cache/client/rule/not_satisfied.go +++ b/cache/client/rule/not_satisfied.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package rule import ( diff --git a/cache/client/rule/rule.go b/cache/client/rule/rule.go index 5cd51331..e2aa64ec 100644 --- a/cache/client/rule/rule.go +++ b/cache/client/rule/rule.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package rule import ( diff --git a/cache/client/rule/satisfied.go b/cache/client/rule/satisfied.go index 07f9f94b..dec708b4 100644 --- a/cache/client/rule/satisfied.go +++ b/cache/client/rule/satisfied.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package rule import ( diff --git a/cache/client/rule/validator.go b/cache/client/rule/validator.go index a9b4db8d..98008ee9 100644 --- a/cache/client/rule/validator.go +++ b/cache/client/rule/validator.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package rule import ( diff --git a/cache/client/ruleset.go b/cache/client/ruleset.go index b91f6c88..fe747bbc 100644 --- a/cache/client/ruleset.go +++ b/cache/client/ruleset.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package client import ( @@ -30,9 +26,8 @@ var DefaultRuleSet = rule.Chained( rule.Header(ruleset.NoCacheRule, ruleset.NoCacheRule), ) -// NoCache called when a particular handler is not valid for cache. -// If this function called inside a handler then the handler is not cached -// even if it's surrounded with the Cache/CacheFunc wrappers. +// NoCache disables the cache for a particular request, +// can be used as a middleware or called manually from the handler. func NoCache(ctx context.Context) { ctx.Header(cfg.NoCacheHeader, "true") } diff --git a/cache/client/utils.go b/cache/client/utils.go index 1ff383f6..33e4bd8c 100644 --- a/cache/client/utils.go +++ b/cache/client/utils.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package client import ( diff --git a/cache/entry/entry.go b/cache/entry/entry.go index c9492b21..8e7cbd9b 100644 --- a/cache/entry/entry.go +++ b/cache/entry/entry.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package entry import ( diff --git a/cache/entry/response.go b/cache/entry/response.go index a7409537..53569c59 100644 --- a/cache/entry/response.go +++ b/cache/entry/response.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package entry // Response is the cached response will be send to the clients diff --git a/cache/entry/util.go b/cache/entry/util.go index 1fd3e868..df9d5b1a 100644 --- a/cache/entry/util.go +++ b/cache/entry/util.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package entry import ( diff --git a/cache/ruleset/ruleset.go b/cache/ruleset/ruleset.go index 9697418c..61ae80a6 100644 --- a/cache/ruleset/ruleset.go +++ b/cache/ruleset/ruleset.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - // Package ruleset provides the basics rules which are being extended by rules. package ruleset diff --git a/cache/uri/uribuilder.go b/cache/uri/uribuilder.go index 66608c2e..383336f3 100644 --- a/cache/uri/uribuilder.go +++ b/cache/uri/uribuilder.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package uri import ( diff --git a/configuration.go b/configuration.go index e946afd6..b87990b7 100644 --- a/configuration.go +++ b/configuration.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package iris import ( @@ -99,11 +95,16 @@ type Configurator func(*Application) // variables for configurators don't need any receivers, functions // for them that need (helps code editors to recognise as variables without parenthesis completion). -// WithoutBanner turns off the write banner on server startup. -var WithoutBanner = func(app *Application) { - app.config.DisableBanner = true +// WithoutStartupLog turns off the information send, once, to the terminal when the main server is open. +var WithoutStartupLog = func(app *Application) { + app.config.DisableStartupLog = true } +// WithoutBanner is a conversion for the `WithoutStartupLog` option. +// +// Turns off the information send, once, to the terminal when the main server is open. +var WithoutBanner = WithoutStartupLog + // WithoutInterruptHandler disables the automatic graceful server shutdown // when control/cmd+C pressed. var WithoutInterruptHandler = func(app *Application) { @@ -163,6 +164,42 @@ func WithCharset(charset string) Configurator { } } +// WithRemoteAddrHeader enables or adds a new or existing request header name +// that can be used to validate the client's real IP. +// +// Existing values are: +// "X-Real-Ip": false, +// "X-Forwarded-For": false, +// "CF-Connecting-IP": false +// +// Look `context.RemoteAddr()` for more. +func WithRemoteAddrHeader(headerName string) Configurator { + return func(app *Application) { + if app.config.RemoteAddrHeaders == nil { + app.config.RemoteAddrHeaders = make(map[string]bool, 0) + } + app.config.RemoteAddrHeaders[headerName] = true + } +} + +// WithoutRemoteAddrHeader disables an existing request header name +// that can be used to validate the client's real IP. +// +// Existing values are: +// "X-Real-Ip": false, +// "X-Forwarded-For": false, +// "CF-Connecting-IP": false +// +// Look `context.RemoteAddr()` for more. +func WithoutRemoteAddrHeader(headerName string) Configurator { + return func(app *Application) { + if app.config.RemoteAddrHeaders == nil { + app.config.RemoteAddrHeaders = make(map[string]bool, 0) + } + app.config.RemoteAddrHeaders[headerName] = false + } +} + // WithOtherValue adds a value based on a key to the Other setting. // // See` Configuration`. @@ -175,7 +212,7 @@ func WithOtherValue(key string, val interface{}) Configurator { } } -// Configuration the whole configuration for an Iris instance +// Configuration the whole configuration for an iris instance // these can be passed via options also, look at the top of this file(configuration.go). // Configuration is a valid OptionSetter. type Configuration struct { @@ -183,10 +220,10 @@ type Configuration struct { // It can be retrieved by the context if needed (i.e router for subdomains) vhost string - // DisableBanner if setted to true then it turns off the write banner on server startup. + // DisableStartupLog if setted to true then it turns off the write banner on server startup. // // Defaults to false. - DisableBanner bool `yaml:"DisableBanner" toml:"DisableBanner"` + DisableStartupLog bool `yaml:"DisableStartupLog" toml:"DisableStartupLog"` // DisableInterruptHandler if setted to true then it disables the automatic graceful server shutdown // when control/cmd+C pressed. // Turn this to true if you're planning to handle this by your own via a custom host.Task. @@ -238,7 +275,7 @@ type Configuration struct { // // Developer may want this option to setted as true in order to manually call the // error handlers when needed via "context.FireStatusCode(>=400)". - // HTTP Custom error handlers are being registered via "framework.OnStatusCode(code, handler)". + // HTTP Custom error handlers are being registered via app.OnErrorCode(code, handler)". // // Defaults to false. DisableAutoFireStatusCode bool `yaml:"DisableAutoFireStatusCode" toml:"DisableAutoFireStatusCode"` @@ -281,7 +318,16 @@ type Configuration struct { // // Defaults to "iris.viewData" ViewDataContextKey string `yaml:"ViewDataContextKey" toml:"ViewDataContextKey"` - + // RemoteAddrHeaders returns the allowed request headers names + // that can be valid to parse the client's IP based on. + // + // Defaults to: + // "X-Real-Ip": false, + // "X-Forwarded-For": false, + // "CF-Connecting-IP": false + // + // Look `context.RemoteAddr()` for more. + RemoteAddrHeaders map[string]bool `yaml:"RemoteAddrHeaders" toml:"RemoteAddrHeaders"` // Other are the custom, dynamic options, can be empty. // This field used only by you to set any app's options you want // or by custom adaptors, it's a way to simple communicate between your adaptors (if any) @@ -379,6 +425,19 @@ func (c Configuration) GetViewDataContextKey() string { return c.ViewDataContextKey } +// GetRemoteAddrHeaders returns the allowed request headers names +// that can be valid to parse the client's IP based on. +// +// Defaults to: +// "X-Real-Ip": false, +// "X-Forwarded-For": false, +// "CF-Connecting-IP": false +// +// Look `context.RemoteAddr()` for more. +func (c Configuration) GetRemoteAddrHeaders() map[string]bool { + return c.RemoteAddrHeaders +} + // GetOther returns the configuration.Other map. func (c Configuration) GetOther() map[string]interface{} { return c.Other @@ -396,8 +455,8 @@ func WithConfiguration(c Configuration) Configurator { return func(app *Application) { main := app.config - if v := c.DisableBanner; v { - main.DisableBanner = v + if v := c.DisableStartupLog; v { + main.DisableStartupLog = v } if v := c.DisableInterruptHandler; v { @@ -448,6 +507,15 @@ func WithConfiguration(c Configuration) Configurator { main.ViewDataContextKey = v } + if v := c.RemoteAddrHeaders; len(v) > 0 { + if main.RemoteAddrHeaders == nil { + main.RemoteAddrHeaders = make(map[string]bool, 0) + } + for key, value := range v { + main.RemoteAddrHeaders[key] = value + } + } + if v := c.Other; len(v) > 0 { if main.Other == nil { main.Other = make(map[string]interface{}, 0) @@ -459,10 +527,10 @@ func WithConfiguration(c Configuration) Configurator { } } -// DefaultConfiguration returns the default configuration for an Iris station, fills the main Configuration +// DefaultConfiguration returns the default configuration for an iris station, fills the main Configuration func DefaultConfiguration() Configuration { return Configuration{ - DisableBanner: false, + DisableStartupLog: false, DisableInterruptHandler: false, DisablePathCorrection: false, EnablePathEscape: false, @@ -475,6 +543,11 @@ func DefaultConfiguration() Configuration { TranslateLanguageContextKey: "iris.language", ViewLayoutContextKey: "iris.viewLayout", ViewDataContextKey: "iris.viewData", - Other: make(map[string]interface{}, 0), + RemoteAddrHeaders: map[string]bool{ + "X-Real-Ip": false, + "X-Forwarded-For": false, + "CF-Connecting-IP": false, + }, + Other: make(map[string]interface{}, 0), } } diff --git a/context.go b/context.go new file mode 100644 index 00000000..673dde23 --- /dev/null +++ b/context.go @@ -0,0 +1,35 @@ +// +build go1.9 + +package iris + +import ( + "github.com/kataras/iris/context" +) + +type ( + // Context is the midle-man server's "object" for the clients. + // + // A New context is being acquired from a sync.Pool on each connection. + // The Context is the most important thing on the iris's http flow. + // + // Developers send responses to the client's request through a Context. + // Developers get request information from the client's request by a Context. + Context = context.Context + // A Handler responds to an HTTP request. + // It writes reply headers and data to the Context.ResponseWriter() and then return. + // Returning signals that the request is finished; + // it is not valid to use the Context after or concurrently with the completion of the Handler call. + // + // Depending on the HTTP client software, HTTP protocol version, + // and any intermediaries between the client and the iris server, + // it may not be possible to read from the Context.Request().Body after writing to the context.ResponseWriter(). + // Cautious handlers should read the Context.Request().Body first, and then reply. + // + // Except for reading the body, handlers should not modify the provided Context. + // + // If Handler panics, the server (the caller of Handler) assumes that the effect of the panic was isolated to the active request. + // It recovers the panic, logs a stack trace to the server error log, and hangs up the connection. + Handler = context.Handler + // A Map is a shortcut of the map[string]interface{}. + Map = context.Map +) diff --git a/context/framework.go b/context/application.go similarity index 62% rename from context/framework.go rename to context/application.go index 43e1f35c..24372941 100644 --- a/context/framework.go +++ b/context/application.go @@ -1,23 +1,21 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package context import ( "io" "net/http" - "github.com/kataras/iris/sessions" + "github.com/sirupsen/logrus" ) -// Application is the context's available Application instance, only things that are allowed to be happen inside the request are lived here. +// Application is the context's owner. +// This interface contains the functions that can be used with safety inside a Handler +// by `context.Application()`. type Application interface { // ConfigurationReadOnly returns all the available configuration values can be used on a request. ConfigurationReadOnly() ConfigurationReadOnly - // Log uses the user's defined logger to log a warning or error message. - Log(format string, a ...interface{}) + // Logger returns the logrus logger instance(pointer) that is being used inside the "app". + Logger() *logrus.Logger // View executes and write the result of a template file to the writer. // @@ -25,12 +23,6 @@ type Application interface { // Returns an error on failure, otherwise nil. View(writer io.Writer, filename string, layout string, bindingData interface{}) error - // SessionManager returns the session manager which contain a Start and Destroy methods - // used inside the context.Session(). - // - // It's ready to use after the RegisterSessions. - SessionManager() (sessions.Sessions, error) - // ServeHTTPC is the internal router, it's visible because it can be used for advanced use cases, // i.e: routing within a foreign context. // diff --git a/context/configuration.go b/context/configuration.go index c67a87cb..3d157014 100644 --- a/context/configuration.go +++ b/context/configuration.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package context // ConfigurationReadOnly can be implemented @@ -72,6 +68,17 @@ type ConfigurationReadOnly interface { // binding data from a middleware or the main handler. GetViewDataContextKey() string + // GetRemoteAddrHeaders returns the allowed request headers names + // that can be valid to parse the client's IP based on. + // + // Defaults to: + // "X-Real-Ip": true, + // "X-Forwarded-For": true, + // "CF-Connecting-IP": false + // + // Look `context.RemoteAddr()` for more. + GetRemoteAddrHeaders() map[string]bool + // GetOther returns the configuration.Other map. GetOther() map[string]interface{} } diff --git a/context/context.go b/context/context.go index eb48219a..b15d214f 100644 --- a/context/context.go +++ b/context/context.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package context import ( @@ -32,7 +28,6 @@ import ( "github.com/kataras/iris/core/errors" "github.com/kataras/iris/core/memstore" - "github.com/kataras/iris/sessions" ) type ( @@ -142,7 +137,7 @@ func (r RequestParams) Len() int { // Context is the midle-man server's "object" for the clients. // // A New context is being acquired from a sync.Pool on each connection. -// The Context is the most important thing on the Iris' http flow. +// The Context is the most important thing on the iris's http flow. // // Developers send responses to the client's request through a Context. // Developers get request information from the client's request a Context. @@ -155,7 +150,7 @@ type Context interface { // BeginRequest is executing once for each request // it should prepare the (new or acquired from pool) context's fields for the new request. // - // To follow the Iris' flow, developer should: + // To follow the iris' flow, developer should: // 1. reset handlers to nil // 2. reset values to empty // 3. reset sessions to nil @@ -165,7 +160,7 @@ type Context interface { BeginRequest(http.ResponseWriter, *http.Request) // EndRequest is executing once after a response to the request was sent and this context is useless or released. // - // To follow the Iris' flow, developer should: + // To follow the iris' flow, developer should: // 1. flush the response writer's result // 2. release the response writer // and any other optional steps, depends on dev's application type. @@ -249,7 +244,7 @@ type Context interface { // Translate is the i18n (localization) middleware's function, // it calls the Get("translate") to return the translated value. // - // Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/i18n + // Example: https://github.com/kataras/iris/tree/master/_examples/miscellaneous/i18n Translate(format string, args ...interface{}) string // +------------------------------------------------------------+ @@ -270,7 +265,16 @@ type Context interface { // Subdomain returns the subdomain of this request, if any. // Note that this is a fast method which does not cover all cases. Subdomain() (subdomain string) - // RemoteAddr tries to return the real client's request IP. + // RemoteAddr tries to parse and return the real client's request IP. + // + // Based on allowed headers names that can be modified from Configuration.RemoteAddrHeaders. + // + // If parse based on these headers fail then it will return the Request's `RemoteAddr` field + // which is filled by the server before the HTTP handler. + // + // Look `Configuration.RemoteAddrHeaders`, + // `Configuration.WithRemoteAddrHeader(...)`, + // `Configuration.WithoutRemoteAddrHeader(...)` for more. RemoteAddr() string // GetHeader returns the request header's value based on its name. GetHeader(name string) string @@ -344,7 +348,7 @@ type Context interface { // NotFound emits an error 404 to the client, using the specific custom error error handler. // Note that you may need to call ctx.StopExecution() if you don't want the next handlers - // to be executed. Next handlers are being executed on Iris because you can alt the + // to be executed. Next handlers are being executed on iris because you can alt the // error code and change it to a more specific one, i.e // users := app.Party("/users") // users.Done(func(ctx context.Context){ if ctx.StatusCode() == 400 { /* custom error code for /users */ }}) @@ -401,9 +405,9 @@ type Context interface { // // Returns the number of bytes written and any write error encountered. WriteString(body string) (int, error) - // WriteWithExpiration like SetBody but it sends with an expiration datetime - // which is managed by the client-side (all major web browsers supports this) - WriteWithExpiration(bodyContent []byte, cType string, modtime time.Time) error + // WriteWithExpiration like Write but it sends with an expiration datetime + // which is refreshed every package-level `StaticCacheDuration` field. + WriteWithExpiration(body []byte, modtime time.Time) (int, error) // StreamWriter registers the given stream writer for populating // response body. // @@ -459,7 +463,7 @@ type Context interface { // // Look .ViewData and .View too. // - // Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/view/context-view-data/ + // Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/ ViewLayout(layoutTmplFile string) // ViewData saves one or more key-value pair in order to be passed if and when .View @@ -479,7 +483,7 @@ type Context interface { // // Look .ViewLayout and .View too. // - // Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/view/context-view-data/ + // Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/ ViewData(key string, value interface{}) // View renders templates based on the adapted view engines. @@ -489,7 +493,7 @@ type Context interface { // // Look: .ViewData and .ViewLayout too. // - // Examples: https://github.com/kataras/iris/tree/master/_examples/intermediate/view/ + // Examples: https://github.com/kataras/iris/tree/master/_examples/view/ View(filename string) error // Binary writes out the raw bytes as binary data. @@ -533,7 +537,7 @@ type Context interface { SendFile(filename string, destinationName string) error // +------------------------------------------------------------+ - // | Cookies, Session and Flashes | + // | Cookies | // +------------------------------------------------------------+ // SetCookie adds a cookie @@ -552,11 +556,6 @@ type Context interface { // on each (request's) cookies' name and value. VisitAllCookies(visitor func(name string, value string)) - // Session returns the current user's Session. - Session() sessions.Session - // SessionDestroy destroys the whole session and removes the session id cookie. - SessionDestroy() - // MaxAge returns the "cache-control" request header's value // seconds as int64 // if header not found or parse failed then it returns -1. @@ -589,7 +588,7 @@ type Context interface { // this transaction scope is only for context's response. // Transactions have their own middleware ecosystem also, look iris.go:UseTransaction. // - // See https://github.com/kataras/iris/tree/master/_examples/advanced/transactions for more + // See https://github.com/kataras/iris/tree/master/_examples/ for more BeginTransaction(pipe func(t *Transaction)) // SkipTransactions if called then skip the rest of the transactions // or all of them if called before the first transaction @@ -613,7 +612,7 @@ type Context interface { // // app.None(...) and app.Routes().Offline(route)/.Online(route, method) // - // Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/route-state + // Example: https://github.com/kataras/iris/tree/master/_examples/routing/route-state // // User can get the response by simple using rec := ctx.Recorder(); rec.Body()/rec.StatusCode()/rec.Header(). // @@ -622,10 +621,10 @@ type Context interface { // It's for extreme use cases, 99% of the times will never be useful for you. Exec(method string, path string) - // Application returns the Iris framework instance which belongs to this context. + // Application returns the iris app instance which belongs to this context. // Worth to notice that this function returns an interface // of the Application, which contains methods that are safe - // to be executed at serve-time. The full framework's fields + // to be executed at serve-time. The full app's fields // and methods are not available here for the developer's safety. Application() Application @@ -735,8 +734,8 @@ func Next(ctx Context) { } } -// LimitRequestBodySize is a middleware which sets a request body size limit for all next handlers -// should be registered before all other handlers +// LimitRequestBodySize is a middleware which sets a request body size limit +// for all next handlers in the chain. var LimitRequestBodySize = func(maxRequestBodySizeBytes int64) Handler { return func(ctx Context) { ctx.SetMaxRequestBodySize(maxRequestBodySizeBytes) @@ -760,12 +759,10 @@ type context struct { params RequestParams // url named parameters values memstore.Store // generic storage, middleware communication - // the underline application framework - framework Application + // the underline application app + app Application // the route's handlers handlers Handlers - // the session, can be nil if never acquired - session sessions.Session // the current position of the handler's chain currentHandlerIndex int } @@ -775,14 +772,14 @@ type context struct { // to a custom one. // // This context is received by the context pool. -func NewContext(framework Application) Context { - return &context{framework: framework} +func NewContext(app Application) Context { + return &context{app: app} } // BeginRequest is executing once for each request // it should prepare the (new or acquired from pool) context's fields for the new request. // -// To follow the Iris' flow, developer should: +// To follow the iris' flow, developer should: // 1. reset handlers to nil // 2. reset store to empty // 3. reset sessions to nil @@ -791,7 +788,6 @@ func NewContext(framework Application) Context { // and any other optional steps, depends on dev's application type. func (ctx *context) BeginRequest(w http.ResponseWriter, r *http.Request) { ctx.handlers = nil // will be filled by router.Serve/HTTP - ctx.session = nil // >> >> by sessions.Session() ctx.values = ctx.values[0:0] // >> >> by context.Values().Set ctx.params.store = ctx.params.store[0:0] ctx.request = r @@ -802,7 +798,7 @@ func (ctx *context) BeginRequest(w http.ResponseWriter, r *http.Request) { // EndRequest is executing once after a response to the request was sent and this context is useless or released. // -// To follow the Iris' flow, developer should: +// To follow the iris' flow, developer should: // 1. flush the response writer's result // 2. release the response writer // and any other optional steps, depends on dev's application type. @@ -961,7 +957,7 @@ func (ctx *context) Values() *memstore.Store { // Translate is the i18n (localization) middleware's function, // it calls the Get("translate") to return the translated value. // -// Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/i18n +// Example: https://github.com/kataras/iris/tree/master/_examples/miscellaneous/i18n func (ctx *context) Translate(format string, args ...interface{}) string { if cb, ok := ctx.values.Get(ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey()).(func(format string, args ...interface{}) string); ok { return cb(format, args...) @@ -1056,8 +1052,8 @@ func (ctx *context) Subdomain() (subdomain string) { subdomain = host[0:index] } - // listening on iris-go.com:80 - // subdomain = iris-go, but it's wrong, it should return "" + // listening on mydomain.com:80 + // subdomain = mydomain, but it's wrong, it should return "" vhost := ctx.Application().ConfigurationReadOnly().GetVHost() if strings.Contains(vhost, subdomain) { // then it's not subdomain return "" @@ -1066,30 +1062,46 @@ func (ctx *context) Subdomain() (subdomain string) { return } -// RemoteAddr tries to return the real client's request IP. +// RemoteAddr tries to parse and return the real client's request IP. +// +// Based on allowed headers names that can be modified from Configuration.RemoteAddrHeaders. +// +// If parse based on these headers fail then it will return the Request's `RemoteAddr` field +// which is filled by the server before the HTTP handler. +// +// Look `Configuration.RemoteAddrHeaders`, +// `Configuration.WithRemoteAddrHeader(...)`, +// `Configuration.WithoutRemoteAddrHeader(...)` for more. func (ctx *context) RemoteAddr() string { - header := ctx.GetHeader("X-Real-Ip") - realIP := strings.TrimSpace(header) - if realIP != "" { - return realIP - } - realIP = ctx.GetHeader("X-Forwarded-For") - idx := strings.IndexByte(realIP, ',') - if idx >= 0 { - realIP = realIP[0:idx] - } - realIP = strings.TrimSpace(realIP) - if realIP != "" { - return realIP + + remoteHeaders := ctx.Application().ConfigurationReadOnly().GetRemoteAddrHeaders() + + for headerName, enabled := range remoteHeaders { + if enabled { + headerValue := ctx.GetHeader(headerName) + // exception needed for 'X-Forwarded-For' only , if enabled. + if headerName == "X-Forwarded-For" { + idx := strings.IndexByte(headerValue, ',') + if idx >= 0 { + headerValue = headerValue[0:idx] + } + } + + realIP := strings.TrimSpace(headerValue) + if realIP != "" { + return realIP + } + } } + addr := strings.TrimSpace(ctx.request.RemoteAddr) - if len(addr) == 0 { - return "" - } - // if addr has port use the net.SplitHostPort otherwise(error occurs) take as it is - if ip, _, err := net.SplitHostPort(addr); err == nil { - return ip + if addr != "" { + // if addr has port use the net.SplitHostPort otherwise(error occurs) take as it is + if ip, _, err := net.SplitHostPort(addr); err == nil { + return ip + } } + return addr } @@ -1160,7 +1172,7 @@ func (ctx *context) StatusCode(statusCode int) { // NotFound emits an error 404 to the client, using the specific custom error error handler. // Note that you may need to call ctx.StopExecution() if you don't want the next handlers -// to be executed. Next handlers are being executed on Iris because you can alt the +// to be executed. Next handlers are being executed on iris because you can alt the // error code and change it to a more specific one, i.e // users := app.Party("/users") // users.Done(func(ctx context.Context){ if ctx.StatusCode() == 400 { /* custom error code for /users */ }}) @@ -1264,24 +1276,6 @@ func (ctx *context) Redirect(urlToRedirect string, statusHeader ...int) { httpStatus = statusHeader[0] } - // comment these because in some cases the the ctx.Request().URL.Path is already updated - // to the new one, so it shows a wrong warning message. - // - // // we don't know the Method of the url to redirect, - // // sure we can find it by reverse routing as we already implemented - // // but it will take too much time for a simple redirect, it doesn't worth it. - // // So we are checking the CURRENT Method for GET, HEAD, CONNECT and TRACE. - // // the - // // Fixes: http: //support.iris-go.com/d/21-wrong-warning-message-while-redirecting - // shouldCheckForCycle := urlToRedirect == ctx.Path() && ctx.Method() == http.MethodGet - // // from POST to GET on the same path will give a warning message but developers don't use the iris.DevLogger - // // for production, so I assume it's OK to let it logs it - // // (it can solve issues when developer redirects to the same handler over and over again) - // // Note: it doesn't stops the redirect, the developer gets what he/she expected. - // if shouldCheckForCycle { - // ctx.Application().Log("warning: redirect from: '%s' to: '%s',\ncurrent method: '%s'", ctx.Path(), urlToRedirect, ctx.Method()) - // } - http.Redirect(ctx.writer, ctx.request, urlToRedirect, httpStatus) } @@ -1356,7 +1350,7 @@ func (ctx *context) ReadForm(formObject interface{}) error { } // or dec := formam.NewDecoder(&formam.DecoderOptions{TagName: "form"}) - // somewhere at the framework level. I did change the tagName to "form" + // somewhere at the app level. I did change the tagName to "form" // inside its source code, so it's not needed for now. return errReadBody.With(formam.Decode(values, formObject)) } @@ -1429,19 +1423,17 @@ func (ctx *context) staticCachePassed(modtime time.Time) bool { } // WriteWithExpiration like Write but it sends with an expiration datetime -// which is managed by the client-side (all major web browsers supports this) -func (ctx *context) WriteWithExpiration(bodyContent []byte, cType string, modtime time.Time) error { +// which is refreshed every package-level `StaticCacheDuration` field. +func (ctx *context) WriteWithExpiration(body []byte, modtime time.Time) (int, error) { + if ctx.staticCachePassed(modtime) { - return nil + return 0, nil } modtimeFormatted := modtime.UTC().Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat()) + ctx.Header(lastModifiedHeaderKey, modtimeFormatted) - ctx.writer.Header().Set(contentTypeHeaderKey, cType) - ctx.writer.Header().Set(lastModifiedHeaderKey, modtimeFormatted) - - _, err := ctx.writer.Write(bodyContent) - return err + return ctx.writer.Write(body) } // StreamWriter registers the given stream writer for populating @@ -1584,7 +1576,7 @@ const ( // // Look .ViewData and .View too. // -// Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/view/context-view-data/ +// Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/ func (ctx *context) ViewLayout(layoutTmplFile string) { ctx.values.Set(ctx.Application().ConfigurationReadOnly().GetViewLayoutContextKey(), layoutTmplFile) } @@ -1606,7 +1598,7 @@ func (ctx *context) ViewLayout(layoutTmplFile string) { // // Look .ViewLayout and .View too. // -// Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/view/context-view-data/ +// Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/ func (ctx *context) ViewData(key string, value interface{}) { viewDataContextKey := ctx.Application().ConfigurationReadOnly().GetViewDataContextKey() if key == "" { @@ -1620,7 +1612,9 @@ func (ctx *context) ViewData(key string, value interface{}) { return } - if data, ok := v.(Map); ok { + if data, ok := v.(map[string]interface{}); ok { + data[key] = value + } else if data, ok := v.(Map); ok { data[key] = value } } @@ -1632,15 +1626,18 @@ func (ctx *context) ViewData(key string, value interface{}) { // // Look: .ViewData and .ViewLayout too. // -// Examples: https://github.com/kataras/iris/tree/master/_examples/intermediate/view/ +// Examples: https://github.com/kataras/iris/tree/master/_examples/view/ func (ctx *context) View(filename string) error { ctx.ContentType(contentHTMLHeaderValue) - layout := ctx.values.GetString(ctx.Application().ConfigurationReadOnly().GetViewLayoutContextKey()) - bindingData := ctx.values.Get(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey()) + cfg := ctx.Application().ConfigurationReadOnly() + + layout := ctx.values.GetString(cfg.GetViewLayoutContextKey()) + bindingData := ctx.values.Get(cfg.GetViewDataContextKey()) err := ctx.Application().View(ctx.writer, filename, layout, bindingData) if err != nil { ctx.StatusCode(http.StatusInternalServerError) + ctx.StopExecution() } return err @@ -1682,6 +1679,36 @@ func (ctx *context) HTML(htmlContents string) (int, error) { return ctx.writer.WriteString(htmlContents) } +// JSON contains the options for the JSON (Context's) Renderer. +type JSON struct { + // http-specific + StreamingJSON bool + // content-specific + UnescapeHTML bool + Indent string + Prefix string +} + +// JSONP contains the options for the JSONP (Context's) Renderer. +type JSONP struct { + // content-specific + Indent string + Callback string +} + +// XML contains the options for the XML (Context's) Renderer. +type XML struct { + // content-specific + Indent string + Prefix string +} + +// Markdown contains the options for the Markdown (Context's) Renderer. +type Markdown struct { + // content-specific + Sanitize bool +} + var ( newLineB = []byte("\n") // the html codes for unescaping @@ -2022,31 +2049,6 @@ func (ctx *context) VisitAllCookies(visitor func(name string, value string)) { } } -// Session returns the current user's Session. -func (ctx *context) Session() sessions.Session { - sessmanager, err := ctx.Application().SessionManager() - if err != nil { - return nil - } - - if ctx.session == nil { - ctx.session = sessmanager.Start(ctx.writer, ctx.request) - } - - return ctx.session -} - -// SessionDestroy destroys the whole session and removes the session id cookie. -func (ctx *context) SessionDestroy() { - if sess := ctx.Session(); sess != nil { - sessmanager, err := ctx.Application().SessionManager() - if err != nil { - return - } - sessmanager.Destroy(ctx.writer, ctx.request) - } -} - var maxAgeExp = regexp.MustCompile(`maxage=(\d+)`) // MaxAge returns the "cache-control" request header's value @@ -2115,7 +2117,7 @@ var errTransactionInterrupted = errors.New("transaction interrupted, recovery fr // this transaction scope is only for context's response. // Transactions have their own middleware ecosystem also, look iris.go:UseTransaction. // -// See https://github.com/kataras/iris/tree/master/_examples/advanced/transactions for more +// See https://github.com/kataras/iris/tree/master/_examples/ for more func (ctx *context) BeginTransaction(pipe func(t *Transaction)) { // do NOT begin a transaction when the previous transaction has been failed // and it was requested scoped or SkipTransactions called manually. @@ -2129,7 +2131,7 @@ func (ctx *context) BeginTransaction(pipe func(t *Transaction)) { t := newTransaction(ctx) // it calls this *context, so the overriding with a new pool's New of context.Context wil not work here. defer func() { if err := recover(); err != nil { - ctx.Application().Log(errTransactionInterrupted.Format(err).Error()) + ctx.Application().Logger().Warnln(errTransactionInterrupted.Format(err).Error()) // complete (again or not , doesn't matters) the scope without loud t.Complete(nil) // we continue as normal, no need to return here* @@ -2181,7 +2183,7 @@ func (ctx *context) TransactionsSkipped() bool { // // app.None(...) and app.Routes().Offline(route)/.Online(route, method) // -// Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/route-state +// Example: https://github.com/kataras/iris/tree/master/_examples/routing/route-state // // User can get the response by simple using rec := ctx.Recorder(); rec.Body()/rec.StatusCode()/rec.Header(). // @@ -2233,13 +2235,13 @@ func (ctx *context) Exec(method string, path string) { } } -// Application returns the Iris framework instance which belongs to this context. +// Application returns the iris app instance which belongs to this context. // Worth to notice that this function returns an interface // of the Application, which contains methods that are safe -// to be executed at serve-time. The full framework's fields +// to be executed at serve-time. The full app's fields // and methods are not available here for the developer's safety. func (ctx *context) Application() Application { - return ctx.framework + return ctx.app } // +--------------------------------------------------------------+ diff --git a/context/gzip_response_writer.go b/context/gzip_response_writer.go index ac3a7142..7a60360c 100644 --- a/context/gzip_response_writer.go +++ b/context/gzip_response_writer.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package context import ( @@ -74,7 +70,7 @@ func releaseGzipResponseWriter(w *GzipResponseWriter) { // GzipResponseWriter is an upgraded response writer which writes compressed data to the underline ResponseWriter. // -// It's a separate response writer because Iris gives you the ability to "fallback" and "roll-back" the gzip encoding if something +// It's a separate response writer because iris gives you the ability to "fallback" and "roll-back" the gzip encoding if something // went wrong with the response, and write http errors in plain form instead. type GzipResponseWriter struct { ResponseWriter diff --git a/context/handler.go b/context/handler.go index 8d0bc79a..d86431b0 100644 --- a/context/handler.go +++ b/context/handler.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package context // A Handler responds to an HTTP request. @@ -10,7 +6,7 @@ package context // it is not valid to use the Context after or concurrently with the completion of the Handler call. // // Depending on the HTTP client software, HTTP protocol version, -// and any intermediaries between the client and the Iris server, +// and any intermediaries between the client and the iris server, // it may not be possible to read from the Context.Request().Body after writing to the context.ResponseWriter(). // Cautious handlers should read the Context.Request().Body first, and then reply. // diff --git a/context/pool.go b/context/pool.go index 469cbc9a..a48d1026 100644 --- a/context/pool.go +++ b/context/pool.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package context import ( diff --git a/context/render_options.go b/context/render_options.go deleted file mode 100644 index c5629ed3..00000000 --- a/context/render_options.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package context - -// +-------------------------------+ -// | Restful | -// +-------------------------------+ - -// JSON contains the options for the JSON (Context's) Renderer. -type JSON struct { - // http-specific - StreamingJSON bool - // content-specific - UnescapeHTML bool - Indent string - Prefix string -} - -// JSONP contains the options for the JSONP (Context's) Renderer. -type JSONP struct { - // content-specific - Indent string - Callback string -} - -// XML contains the options for the XML (Context's) Renderer. -type XML struct { - // content-specific - Indent string - Prefix string -} - -// Markdown contains the options for the Markdown (Context's) Renderer. -type Markdown struct { - // content-specific - Sanitize bool -} diff --git a/context/response_recorder.go b/context/response_recorder.go index 03820325..8af7b17e 100644 --- a/context/response_recorder.go +++ b/context/response_recorder.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package context import ( diff --git a/context/response_writer.go b/context/response_writer.go index 26408514..86555f22 100644 --- a/context/response_writer.go +++ b/context/response_writer.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package context import ( @@ -55,7 +51,7 @@ type ResponseWriter interface { StatusCode() int // Written should returns the total length of bytes that were being written to the client. - // In addition Iris provides some variables to help low-level actions: + // In addition iris provides some variables to help low-level actions: // NoWritten, means that nothing were written yet and the response writer is still live. // StatusCodeWritten, means that status code were written but no other bytes are written to the client, response writer may closed. // > 0 means that the reply was written and it's the total number of bytes were written. @@ -138,7 +134,7 @@ func (w *responseWriter) EndResponse() { } // Written should returns the total length of bytes that were being written to the client. -// In addition Iris provides some variables to help low-level actions: +// In addition iris provides some variables to help low-level actions: // NoWritten, means that nothing were written yet and the response writer is still live. // StatusCodeWritten, means that status code were written but no other bytes are written to the client, response writer may closed. // > 0 means that the reply was written and it's the total number of bytes were written. diff --git a/context/transaction.go b/context/transaction.go index 7cf8b347..a152b437 100644 --- a/context/transaction.go +++ b/context/transaction.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package context // TransactionErrResult could be named also something like 'MaybeError', diff --git a/core/errors/errors.go b/core/errors/errors.go index fb3665bb..d96f277a 100644 --- a/core/errors/errors.go +++ b/core/errors/errors.go @@ -1,40 +1,62 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package errors import ( "fmt" "runtime" + "strings" + + "github.com/satori/go.uuid" ) var ( // Prefix the error prefix, applies to each error's message. Prefix = "" - // NewLine adds a new line to the end of each error's message - // defaults to true - NewLine = true ) // Error holds the error message, this message never really changes type Error struct { - message string - appended bool + // ID returns the unique id of the error, it's needed + // when we want to check if a specific error returned + // but the `Error() string` value is not the same because the error may be dynamic + // by a `Format` call. + ID string `json:"id"` + // The message of the error. + Message string `json:"message"` + // Apennded is true whenever it's a child error. + Appended bool `json:"appended"` + // Stack returns the list of the errors that are shown at `Error() string`. + Stack []Error `json:"stack"` // filled on AppendX. } // New creates and returns an Error with a pre-defined user output message // all methods below that doesn't accept a pointer receiver because actually they are not changing the original message -func New(errMsg string) *Error { - if NewLine { - errMsg += "\n" +func New(errMsg string) Error { + return Error{ + ID: uuid.NewV4().String(), + Message: Prefix + errMsg, } - return &Error{message: Prefix + errMsg} +} + +// Equal returns true if "e" and "e2" are matched, by their IDs. +// It will always returns true if the "e2" is a children of "e" +// or the error messages are exactly the same, otherwise false. +func (e Error) Equal(e2 Error) bool { + return e.ID == e2.ID || e.Error() == e2.Error() +} + +// Empty returns true if the "e" Error has no message on its stack. +func (e Error) Empty() bool { + return e.Message == "" +} + +// NotEmpty returns true if the "e" Error has got a non-empty message on its stack. +func (e Error) NotEmpty() bool { + return !e.Empty() } // String returns the error message func (e Error) String() string { - return e.message + return e.Message } // Error returns the message of the actual error @@ -46,34 +68,53 @@ func (e Error) Error() string { // Format returns a formatted new error based on the arguments // it does NOT change the original error's message func (e Error) Format(a ...interface{}) Error { - e.message = fmt.Sprintf(e.message, a...) + e.Message = fmt.Sprintf(e.Message, a...) + return e +} + +func omitNewLine(message string) string { + if strings.HasSuffix(message, "\n") { + return message[0 : len(message)-2] + } else if strings.HasSuffix(message, "\\n") { + return message[0 : len(message)-3] + } + return message +} + +// AppendInline appends an error to the stack. +// It doesn't try to append a new line if needed. +func (e Error) AppendInline(format string, a ...interface{}) Error { + msg := fmt.Sprintf(format, a...) + e.Message += msg + e.Appended = true + e.Stack = append(e.Stack, New(omitNewLine(msg))) return e } // Append adds a message to the predefined error message and returns a new error // it does NOT change the original error's message func (e Error) Append(format string, a ...interface{}) Error { - // eCp := *e - if NewLine { - format += "\n" + // if new line is false then append this error but first + // we need to add a new line to the first, if it was true then it has the newline already. + if e.Message != "" { + e.Message += "\n" } - e.message += fmt.Sprintf(format, a...) - e.appended = true - return e + + return e.AppendInline(format, a...) } -// AppendErr adds an error's message to the predefined error message and returns a new error +// AppendErr adds an error's message to the predefined error message and returns a new error. // it does NOT change the original error's message func (e Error) AppendErr(err error) Error { return e.Append(err.Error()) } -// IsAppended returns true if the Error instance is created using original's Error.Append/AppendErr func -func (e Error) IsAppended() bool { - return e.appended +// HasStack returns true if the Error instance is created using Append/AppendInline/AppendErr funcs. +func (e Error) HasStack() bool { + return len(e.Stack) > 0 } -// With does the same thing as Format but it receives an error type which if it's nil it returns a nil error +// With does the same thing as Format but it receives an error type which if it's nil it returns a nil error. func (e Error) With(err error) error { if err == nil { return nil @@ -82,15 +123,15 @@ func (e Error) With(err error) error { return e.Format(err.Error()) } -// Panic output the message and after panics +// Panic output the message and after panics. func (e Error) Panic() { _, fn, line, _ := runtime.Caller(1) - errMsg := e.message + errMsg := e.Message errMsg += "\nCaller was: " + fmt.Sprintf("%s:%d", fn, line) panic(errMsg) } -// Panicf output the formatted message and after panics +// Panicf output the formatted message and after panics. func (e Error) Panicf(args ...interface{}) { _, fn, line, _ := runtime.Caller(1) errMsg := e.Format(args...).Error() diff --git a/core/errors/errors_test.go b/core/errors/errors_test.go index 69794b70..8c03eb31 100644 --- a/core/errors/errors_test.go +++ b/core/errors/errors_test.go @@ -13,26 +13,16 @@ var errUserAlreadyExists = errors.New(errMessage) var userMail = "user1@mail.go" var expectedUserAlreadyExists = "User with mail: user1@mail.go already exists" -func getNewLine() string { - if errors.NewLine { - return "\n" - } - return "" -} - func ExampleError() { - fmt.Print(errUserAlreadyExists.Format(userMail)) - // first output first Output line + fmt.Print(errUserAlreadyExists.Format(userMail).Append("Please change your mail addr")) - // second output second and third Output lines // Output: // User with mail: user1@mail.go already exists - // User with mail: user1@mail.go already exists // Please change your mail addr } -func do(method string, testErr *errors.Error, expectingMsg string, t *testing.T) { +func do(method string, testErr errors.Error, expectingMsg string, t *testing.T) { formattedErr := func() error { return testErr.Format(userMail) }() @@ -43,56 +33,51 @@ func do(method string, testErr *errors.Error, expectingMsg string, t *testing.T) } func TestFormat(t *testing.T) { - expected := errors.Prefix + expectedUserAlreadyExists + getNewLine() + expected := errors.Prefix + expectedUserAlreadyExists do("Format Test", errUserAlreadyExists, expected, t) } func TestAppendErr(t *testing.T) { - errors.NewLine = true - errors.Prefix = "error: " errChangeMailMsg := "Please change your mail addr" - errChangeMail := fmt.Errorf(errChangeMailMsg) // test go standard error - expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + errChangeMailMsg + getNewLine() // first Prefix and last newline lives inside do + errChangeMail := fmt.Errorf(errChangeMailMsg) // test go standard error errAppended := errUserAlreadyExists.AppendErr(errChangeMail) - do("Append Test Standard error type", &errAppended, expectedErrorMessage, t) + expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + "\n" + errChangeMailMsg + + do("Append Test Standard error type", errAppended, expectedErrorMessage, t) } func TestAppendError(t *testing.T) { - errors.NewLine = true errors.Prefix = "error: " errChangeMailMsg := "Please change your mail addr" - errChangeMail := errors.New(errChangeMailMsg) // test Error struct - expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + errChangeMail.Error() + getNewLine() // first Prefix and last newline lives inside do + errChangeMail := errors.New(errChangeMailMsg) + errAppended := errUserAlreadyExists.AppendErr(errChangeMail) - do("Append Test Error type", &errAppended, expectedErrorMessage, t) + expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + "\n" + errChangeMail.Error() + + do("Append Test Error type", errAppended, expectedErrorMessage, t) } func TestAppend(t *testing.T) { - errors.NewLine = true errors.Prefix = "error: " errChangeMailMsg := "Please change your mail addr" - expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + errChangeMailMsg + getNewLine() // first Prefix and last newline lives inside do + expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + "\n" + errChangeMailMsg errAppended := errUserAlreadyExists.Append(errChangeMailMsg) - do("Append Test string Message", &errAppended, expectedErrorMessage, t) + do("Append Test string Message", errAppended, expectedErrorMessage, t) } func TestNewLine(t *testing.T) { - errors.NewLine = false - - errNoNewLine := errors.New(errMessage) + err := errors.New(errMessage) expected := errors.Prefix + expectedUserAlreadyExists - do("NewLine Test", errNoNewLine, expected, t) - - errors.NewLine = true + do("NewLine Test", err, expected, t) } func TestPrefix(t *testing.T) { errors.Prefix = "MyPrefix: " errUpdatedPrefix := errors.New(errMessage) - expected := errors.Prefix + expectedUserAlreadyExists + "\n" + expected := errors.Prefix + expectedUserAlreadyExists do("Prefix Test with "+errors.Prefix, errUpdatedPrefix, expected, t) } diff --git a/core/errors/reporter.go b/core/errors/reporter.go new file mode 100644 index 00000000..8da3f59a --- /dev/null +++ b/core/errors/reporter.go @@ -0,0 +1,141 @@ +package errors + +import ( + "sync" +) + +// StackError contains the Stack method. +type StackError interface { + Stack() []Error + Error() string +} + +// PrintAndReturnErrors prints the "err" to the given "printer", +// printer will be called multiple times if the "err" is a StackError, where it contains more than one error. +func PrintAndReturnErrors(err error, printer func(string, ...interface{})) error { + if err == nil || err.Error() == "" { + return nil + } + + if stackErr, ok := err.(StackError); ok { + if len(stackErr.Stack()) == 0 { + return nil + } + + stack := stackErr.Stack() + + for _, e := range stack { + if e.HasStack() { + for _, es := range e.Stack { + printer("%v", es) + } + continue + } + printer("%v", e) + } + + return stackErr + } + + printer("%v", err) + return err +} + +// Reporter is a helper structure which can +// stack errors and prints them to a printer of func(string). +type Reporter struct { + mu sync.Mutex + wrapper Error +} + +// NewReporter returns a new empty error reporter. +func NewReporter() *Reporter { + return &Reporter{wrapper: New("")} +} + +// AddErr adds an error to the error stack. +// if "err" is a StackError then +// each of these errors will be printed as individual. +func (r *Reporter) AddErr(err error) { + if err == nil { + return + } + + if stackErr, ok := err.(StackError); ok { + r.addStack(stackErr.Stack()) + return + } + + r.mu.Lock() + r.wrapper = r.wrapper.AppendErr(err) + r.mu.Unlock() +} + +// Add adds a formatted message as an error to the error stack. +func (r *Reporter) Add(format string, a ...interface{}) { + // usually used as: "module: %v", err so + // check if the first argument is error and if that error is empty then don't add it. + if len(a) > 0 { + f := a[0] + if e, ok := f.(interface { + Error() string + }); ok { + if e.Error() == "" { + return + } + } + } + + r.mu.Lock() + r.wrapper = r.wrapper.Append(format, a...) + r.mu.Unlock() +} + +// Describe same as `Add` but if "err" is nil then it does nothing. +func (r *Reporter) Describe(format string, err error) { + if err == nil { + return + } + if stackErr, ok := err.(StackError); ok { + r.addStack(stackErr.Stack()) + return + } + + r.Add(format, err) +} + +// PrintStack prints all the errors to the given "printer". +// Returns itself in order to be used as printer and return the full error in the same time. +func (r Reporter) PrintStack(printer func(string, ...interface{})) error { + return PrintAndReturnErrors(r, printer) +} + +// Stack returns the list of the errors in the stack. +func (r Reporter) Stack() []Error { + return r.wrapper.Stack +} + +func (r *Reporter) addStack(stack []Error) { + for _, e := range stack { + if e.Error() == "" { + continue + } + r.mu.Lock() + r.wrapper = r.wrapper.AppendErr(e) + r.mu.Unlock() + } +} + +// Error implements the error, returns the full error string. +func (r Reporter) Error() string { + return r.wrapper.Error() +} + +// Return returns nil if the error is empty, otherwise returns the full error. +func (r Reporter) Return() error { + if r.Error() == "" { + return nil + } + + return r +} diff --git a/core/errors/reporter_test.go b/core/errors/reporter_test.go new file mode 100644 index 00000000..c72eabb4 --- /dev/null +++ b/core/errors/reporter_test.go @@ -0,0 +1,26 @@ +// black-box testing +package errors_test + +import ( + "testing" + + "github.com/kataras/iris/core/errors" +) + +func TestReporterAdd(t *testing.T) { + errors.Prefix = "" + + r := errors.NewReporter() + + tests := []string{"err1", "err3", "err4\nerr5"} + for _, tt := range tests { + r.Add(tt) + } + + for i, e := range r.Stack() { + tt := tests[i] + if expected, got := tt, e.Error(); expected != got { + t.Fatalf("[%d] expected %s but got %s", i, expected, got) + } + } +} diff --git a/core/handlerconv/from_std.go b/core/handlerconv/from_std.go index b7bd3b94..3a0b24e1 100644 --- a/core/handlerconv/from_std.go +++ b/core/handlerconv/from_std.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package handlerconv import ( @@ -78,12 +74,12 @@ func FromStd(handler interface{}) context.Handler { func FromStdWithNext(h func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)) context.Handler { return func(ctx context.Context) { // take the next handler in route's chain - nextIrisHandler := ctx.NextHandler() - if nextIrisHandler != nil { + nextIonHandler := ctx.NextHandler() + if nextIonHandler != nil { executed := false // we need to watch this in order to StopExecution from all next handlers // if this next handler is not executed by the third-party net/http next-style Handlers. nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - nextIrisHandler(ctx) + nextIonHandler(ctx) executed = true }) diff --git a/core/host/proxy.go b/core/host/proxy.go index 29991c40..abacfa30 100644 --- a/core/host/proxy.go +++ b/core/host/proxy.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package host import ( @@ -11,7 +7,7 @@ import ( "net/url" "strings" - "github.com/kataras/iris/core/nettools" + "github.com/kataras/iris/core/netutil" ) func singleJoiningSlash(a, b string) string { @@ -52,7 +48,7 @@ func ProxyHandler(target *url.URL) *httputil.ReverseProxy { } p := &httputil.ReverseProxy{Director: director} - if nettools.IsLoopbackHost(target.Host) { + if netutil.IsLoopbackHost(target.Host) { transport := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } diff --git a/core/host/scheduler.go b/core/host/scheduler.go deleted file mode 100644 index 68cae279..00000000 --- a/core/host/scheduler.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package host - -import ( - "sync/atomic" -) - -type task struct { - runner TaskRunner - proc TaskProcess - - // atomic-accessed, if != 0 means that is already - // canceled before it ever ran, this happens to interrupt handlers too. - alreadyCanceled int32 - Cancel func() -} - -func (t *task) isCanceled() bool { - return atomic.LoadInt32(&t.alreadyCanceled) != 0 -} - -// Scheduler is a type of an event emmiter. -// Can register a specific task for a specific event -// when host is starting the server or host is interrupted by CTRL+C/CMD+C. -// It's being used internally on host supervisor. -type Scheduler struct { - onServeTasks []*task - onInterruptTasks []*task -} - -// TaskCancelFunc cancels a Task when called. -type TaskCancelFunc func() - -// Schedule schedule/registers a Task, -// it will be executed/run to when host starts the server -// or when host is interrupted by CTRL+C/CMD+C based on the TaskRunner type. -// -// See `OnInterrupt` and `ScheduleFunc` too. -func (s *Scheduler) Schedule(runner TaskRunner) TaskCancelFunc { - - t := new(task) - t.runner = runner - t.Cancel = func() { - // it's not running yet, so if canceled now - // set to already canceled to not run it at all. - atomic.StoreInt32(&t.alreadyCanceled, 1) - } - - if _, ok := runner.(OnInterrupt); ok { - s.onInterruptTasks = append(s.onInterruptTasks, t) - } else { - s.onServeTasks = append(s.onServeTasks, t) - } - - return func() { - t.Cancel() - } -} - -// ScheduleFunc schedule/registers a task function, -// it will be executed/run to when host starts the server -// or when host is interrupted by CTRL+C/CMD+C based on the TaskRunner type. -// -// See `OnInterrupt` and `ScheduleFunc` too. -func (s *Scheduler) ScheduleFunc(runner func(TaskProcess)) TaskCancelFunc { - return s.Schedule(TaskRunnerFunc(runner)) -} - -func cancelTasks(tasks []*task) { - for _, t := range tasks { - if atomic.LoadInt32(&t.alreadyCanceled) != 0 { - continue // canceled, don't run it - } - go t.Cancel() - } -} - -// CancelOnServeTasks cancels all tasks that are scheduled to run when -// host is starting the server, when the server is alive and online. -func (s *Scheduler) CancelOnServeTasks() { - cancelTasks(s.onServeTasks) -} - -// CancelOnInterruptTasks cancels all tasks that are scheduled to run when -// host is being interrupted by CTRL+C/CMD+C, when the server is alive and online as well. -func (s *Scheduler) CancelOnInterruptTasks() { - cancelTasks(s.onInterruptTasks) -} - -func runTaskNow(task *task, host TaskHost) { - proc := newTaskProcess(host) - task.proc = proc - task.Cancel = func() { - proc.canceledChan <- struct{}{} - } - - go task.runner.Run(proc) -} - -func runTasks(tasks []*task, host TaskHost) { - for _, t := range tasks { - if t.isCanceled() { - continue - } - runTaskNow(t, host) - } -} - -func (s *Scheduler) runOnServe(host TaskHost) { - runTasks(s.onServeTasks, host) -} - -func (s *Scheduler) runOnInterrupt(host TaskHost) { - runTasks(s.onInterruptTasks, host) -} - -func (s *Scheduler) visit(visitor func(*task)) { - for _, t := range s.onServeTasks { - visitor(t) - } - - for _, t := range s.onInterruptTasks { - visitor(t) - } -} - -func (s *Scheduler) notifyShutdown() { - s.visit(func(t *task) { - go func() { - t.proc.Host().doneChan <- struct{}{} - }() - }) -} - -func (s *Scheduler) notifyErr(err error) { - s.visit(func(t *task) { - go func() { - t.proc.Host().errChan <- err - }() - }) -} - -// CopyTo copies all tasks from "s" to "to" Scheduler. -// It doesn't care about anything else. -func (s *Scheduler) CopyTo(to *Scheduler) { - s.visit(func(t *task) { - rnner := t.runner - to.Schedule(rnner) - }) -} diff --git a/core/host/scheduler_test.go b/core/host/scheduler_test.go deleted file mode 100644 index 7e2b1a99..00000000 --- a/core/host/scheduler_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// white-box testing -package host - -import ( - "context" - "fmt" - "log" - "net" - "net/http" - "os" - "time" -) - -type myTestTask struct { - delay time.Duration - logger *log.Logger -} - -func (m myTestTask) Run(proc TaskProcess) { - ticker := time.NewTicker(m.delay) - defer ticker.Stop() - rans := 0 - for { - select { - case _, ok := <-ticker.C: - { - if !ok { - m.logger.Println("ticker issue, closed channel, exiting from this task...") - return - } - rans++ - m.logger.Println(fmt.Sprintf("%d", rans)) - } - case <-proc.Done(): - { - m.logger.Println("canceled, exiting from task AND SHUTDOWN the server...") - proc.Host().Shutdown(context.TODO()) - return - } - } - } -} - -func SchedulerSchedule() { - h := New(&http.Server{ - Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - }), - }) - logger := log.New(os.Stdout, "Supervisor: ", 0) - - delaySeconds := 2 - - mytask := myTestTask{ - delay: time.Duration(delaySeconds) * time.Second, - logger: logger, - } - - cancel := h.Schedule(mytask) - ln, err := net.Listen("tcp4", ":9090") - if err != nil { - panic(err.Error()) - } - - logger.Println("server started...") - logger.Println("we will cancel the task after 2 runs (the third will be canceled)") - cancelAfterRuns := 2 - time.AfterFunc(time.Duration(delaySeconds*cancelAfterRuns+(delaySeconds/2))*time.Second, func() { - cancel() - logger.Println("cancel sent") - }) - h.Serve(ln) - - // Output: - // Supervisor: server started... - // Supervisor: we will cancel the task after 2 runs (the third will be canceled) - // Supervisor: 1 - // Supervisor: 2 - // Supervisor: cancel sent - // Supervisor: canceled, exiting from task AND SHUTDOWN the server... -} diff --git a/core/host/supervisor.go b/core/host/supervisor.go index da22258f..7d98f4ba 100644 --- a/core/host/supervisor.go +++ b/core/host/supervisor.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package host import ( @@ -9,14 +5,11 @@ import ( "crypto/tls" "net" "net/http" - "os" - "os/signal" "sync" "sync/atomic" - "syscall" "github.com/kataras/iris/core/errors" - "github.com/kataras/iris/core/nettools" + "github.com/kataras/iris/core/netutil" "golang.org/x/crypto/acme/autocert" ) @@ -25,16 +18,17 @@ import ( // // Interfaces are separated to return relative functionality to them. type Supervisor struct { - Scheduler - server *http.Server + Server *http.Server closedManually int32 // future use, accessed atomically (non-zero means we've called the Shutdown) - - shouldWait int32 // non-zero means that the host should wait for unblocking - unblockChan chan struct{} - shutdownChan chan struct{} - errChan chan error + manuallyTLS bool // we need that in order to determinate what to output on the console before the server begin. + shouldWait int32 // non-zero means that the host should wait for unblocking + unblockChan chan struct{} mu sync.Mutex + + onServe []func(TaskHost) + onErr []func(error) + onShutdown []func() } // New returns a new host supervisor @@ -46,10 +40,8 @@ type Supervisor struct { // to return and exit and restore the flow too. func New(srv *http.Server) *Supervisor { return &Supervisor{ - server: srv, - unblockChan: make(chan struct{}, 1), - shutdownChan: make(chan struct{}), - errChan: make(chan error), + Server: srv, + unblockChan: make(chan struct{}, 1), } } @@ -83,78 +75,73 @@ func (su *Supervisor) isWaiting() bool { return atomic.LoadInt32(&su.shouldWait) != 0 } -// Done is being received when in server Shutdown. -// This can be used to gracefully shutdown connections that have -// undergone NPN/ALPN protocol upgrade or that have been hijacked. -// This function should start protocol-specific graceful shutdown, -// but should not wait for shutdown to complete. -func (su *Supervisor) Done() <-chan struct{} { - return su.shutdownChan +func (su *Supervisor) newListener() (net.Listener, error) { + // this will not work on "unix" as network + // because UNIX doesn't supports the kind of + // restarts we may want for the server. + // + // User still be able to call .Serve instead. + l, err := netutil.TCPKeepAlive(su.Server.Addr) + if err != nil { + return nil, err + } + + // here we can check for sure, without the need of the supervisor's `manuallyTLS` field. + if netutil.IsTLS(su.Server) { + // means tls + tlsl := tls.NewListener(l, su.Server.TLSConfig) + return tlsl, nil + } + + return l, nil } -// Err refences to the return value of Server's .Serve, not the server's specific error logger. -func (su *Supervisor) Err() <-chan error { - return su.errChan -} - -func (su *Supervisor) notifyShutdown() { - go func() { - su.shutdownChan <- struct{}{} - }() - - su.Scheduler.notifyShutdown() +// RegisterOnError registers a function to call when errors occured by the underline http server. +func (su *Supervisor) RegisterOnError(cb func(error)) { + su.mu.Lock() + su.onErr = append(su.onErr, cb) + su.mu.Unlock() } func (su *Supervisor) notifyErr(err error) { // if err == http.ErrServerClosed { + // su.notifyShutdown() // return // } - go func() { - su.errChan <- err - }() - - su.Scheduler.notifyErr(err) + su.mu.Lock() + for _, f := range su.onErr { + go f(err) + } + su.mu.Unlock() +} + +// RegisterOnServe registers a function to call on +// Serve/ListenAndServe/ListenAndServeTLS/ListenAndServeAutoTLS. +func (su *Supervisor) RegisterOnServe(cb func(TaskHost)) { + su.mu.Lock() + su.onServe = append(su.onServe, cb) + su.mu.Unlock() +} + +func (su *Supervisor) notifyServe(host TaskHost) { + su.mu.Lock() + for _, f := range su.onServe { + go f(host) + } + su.mu.Unlock() } -/// TODO: // Remove all channels, do it with events // or with channels but with a different channel on each task proc // I don't know channels are not so safe, when go func and race risk.. // so better with callbacks.... func (su *Supervisor) supervise(blockFunc func() error) error { - // println("Running Serve from Supervisor") - - // su.server: in order to Serve and Shutdown the underline server and no re-run the supervisors when .Shutdown -> .Serve. - // su.GetBlocker: set the Block() and Unblock(), which are checked after a shutdown or error. - // su.GetNotifier: only one supervisor is allowed to be notified about Close/Shutdown and Err. - // su.log: set this builder's logger in order to supervisor to be able to share a common logger. host := createTaskHost(su) - // run the list of supervisors in different go-tasks by-design. - su.Scheduler.runOnServe(host) - if len(su.Scheduler.onInterruptTasks) > 0 { - // this can't be moved to the task interrupt's `Run` function - // because it will not catch more than one ctrl/cmd+c, so - // we do it here. These tasks are canceled already too. - 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.SIGKILL - os.Kill, - syscall.SIGKILL, // register that too, it should be ok - // kill -SIGTERM XXXX - syscall.SIGTERM, - ) - select { - case <-ch: - su.Scheduler.runOnInterrupt(host) - } - }() - } + su.notifyServe(host) + + tryStartInterruptNotifier() err := blockFunc() su.notifyErr(err) @@ -172,26 +159,6 @@ func (su *Supervisor) supervise(blockFunc func() error) error { return err // start the server } -func (su *Supervisor) newListener() (net.Listener, error) { - // this will not work on "unix" as network - // because UNIX doesn't supports the kind of - // restarts we may want for the server. - // - // User still be able to call .Serve instead. - l, err := nettools.TCPKeepAlive(su.server.Addr) - if err != nil { - return nil, err - } - - if nettools.IsTLS(su.server) { - // means tls - tlsl := tls.NewListener(l, su.server.TLSConfig) - return tlsl, nil - } - - return l, nil -} - // Serve accepts incoming connections on the Listener l, creating a // new service goroutine for each. The service goroutines read requests and // then call su.server.Handler to reply to them. @@ -204,7 +171,8 @@ func (su *Supervisor) newListener() (net.Listener, error) { // Serve always returns a non-nil error. After Shutdown or Close, the // returned error is http.ErrServerClosed. func (su *Supervisor) Serve(l net.Listener) error { - return su.supervise(func() error { return su.server.Serve(l) }) + + return su.supervise(func() error { return su.Server.Serve(l) }) } // ListenAndServe listens on the TCP network address addr @@ -240,8 +208,8 @@ func (su *Supervisor) ListenAndServeTLS(certFile string, keyFile string) error { } setupHTTP2(cfg) - su.server.TLSConfig = cfg - + su.Server.TLSConfig = cfg + su.manuallyTLS = true return su.ListenAndServe() } @@ -256,10 +224,33 @@ func (su *Supervisor) ListenAndServeAutoTLS() error { cfg := new(tls.Config) cfg.GetCertificate = autoTLSManager.GetCertificate setupHTTP2(cfg) - su.server.TLSConfig = cfg + su.Server.TLSConfig = cfg + su.manuallyTLS = true return su.ListenAndServe() } +// RegisterOnShutdown registers a function to call on Shutdown. +// This can be used to gracefully shutdown connections that have +// undergone NPN/ALPN protocol upgrade or that have been hijacked. +// This function should start protocol-specific graceful shutdown, +// but should not wait for shutdown to complete. +func (su *Supervisor) RegisterOnShutdown(cb func()) { + // when go1.9: replace the following lines with su.Server.RegisterOnShutdown(f) + su.mu.Lock() + su.onShutdown = append(su.onShutdown, cb) + su.mu.Unlock() +} + +func (su *Supervisor) notifyShutdown() { + // when go1.9: remove the lines below + su.mu.Lock() + for _, f := range su.onShutdown { + go f() + } + su.mu.Unlock() + // end +} + // Shutdown gracefully shuts down the server without interrupting any // active connections. Shutdown works by first closing all open // listeners, then closing all idle connections, and then waiting @@ -272,9 +263,7 @@ func (su *Supervisor) ListenAndServeAutoTLS() error { // separately notify such long-lived connections of shutdown and wait // for them to close, if desired. func (su *Supervisor) Shutdown(ctx context.Context) error { - // println("Running Shutdown from Supervisor") - atomic.AddInt32(&su.closedManually, 1) // future-use su.notifyShutdown() - return su.server.Shutdown(ctx) + return su.Server.Shutdown(ctx) } diff --git a/core/host/supervisor_task_example_test.go b/core/host/supervisor_task_example_test.go new file mode 100644 index 00000000..9c83e8a1 --- /dev/null +++ b/core/host/supervisor_task_example_test.go @@ -0,0 +1,118 @@ +// 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) + su.Shutdown(context.TODO()) + 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 { + select { + case _, ok := <-ticker.C: + { + if !ok { + m.logger.Println("ticker issue, closed channel, exiting from this task...") + return + } + 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") + host.Serve() // restart + + }) + + } + } + } +} + +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: 6 * 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 index 2c01f721..47b26648 100644 --- a/core/host/supervisor_test.go +++ b/core/host/supervisor_test.go @@ -49,7 +49,7 @@ func newTester(t *testing.T, baseURL string, handler http.Handler) *httpexpect.E return httpexpect.WithConfig(testConfiguration) } -func testSupervisor(t *testing.T, creator func(*http.Server, []TaskRunner) *Supervisor) { +func testSupervisor(t *testing.T, creator func(*http.Server, []func(TaskHost)) *Supervisor) { loggerOutput := &bytes.Buffer{} logger := log.New(loggerOutput, "", 0) const ( @@ -76,11 +76,11 @@ func testSupervisor(t *testing.T, creator func(*http.Server, []TaskRunner) *Supe t.Fatal(err) } - helloMe := TaskRunnerFunc(func(proc TaskProcess) { + helloMe := func(_ TaskHost) { logger.Print(expectedHelloMessage) - }) + } - host := creator(srv, []TaskRunner{helloMe}) + host := creator(srv, []func(TaskHost){helloMe}) defer host.Shutdown(context.TODO()) go host.Serve(ln) @@ -99,10 +99,10 @@ func testSupervisor(t *testing.T, creator func(*http.Server, []TaskRunner) *Supe } } func TestSupervisor(t *testing.T) { - testSupervisor(t, func(srv *http.Server, tasks []TaskRunner) *Supervisor { + testSupervisor(t, func(srv *http.Server, tasks []func(TaskHost)) *Supervisor { su := New(srv) for _, t := range tasks { - su.Schedule(t) + su.RegisterOnServe(t) } return su diff --git a/core/host/task.go b/core/host/task.go index 0697dfc1..7eca97cb 100644 --- a/core/host/task.go +++ b/core/host/task.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package host // the 24hour name was "Supervisor" but it's not cover its usage @@ -10,57 +6,61 @@ package host // supervisor. import ( "context" - "github.com/kataras/iris/core/nettools" + "fmt" + "io" "net/http" + "runtime" + "time" + + "github.com/kataras/iris/core/netutil" ) -type ( - // FlowController exports the `DeferFlow` - // and `RestoreFlow` capabilities. - // Read more at Supervisor. - FlowController interface { - DeferFlow() - RestoreFlow() +// WriteStartupLogOnServe is a task which accepts a logger(io.Writer) +// and logs the listening address +// by a generated message based on the host supervisor's server and writes it to the "w". +// This function should be registered on Serve. +func WriteStartupLogOnServe(w io.Writer) func(TaskHost) { + return func(h TaskHost) { + guessScheme := netutil.ResolveScheme(h.Supervisor.manuallyTLS) + listeningURI := netutil.ResolveURL(guessScheme, h.Supervisor.Server.Addr) + interruptkey := "CTRL" + if runtime.GOOS == "darwin" { + interruptkey = "CMD" + } + w.Write([]byte(fmt.Sprintf("Now listening on: %s\nApplication started. Press %s+C to shut down.\n", + listeningURI, interruptkey))) } -) +} + +// ShutdownOnInterrupt terminates the supervisor and its underline server when CMD+C/CTRL+C pressed. +// This function should be registerd on Interrupt. +func ShutdownOnInterrupt(su *Supervisor, shutdownTimeout time.Duration) func() { + return func() { + ctx, cancel := context.WithTimeout(context.TODO(), shutdownTimeout) + defer cancel() + su.Shutdown(ctx) + su.RestoreFlow() + } +} // TaskHost contains all the necessary information // about the host supervisor, its server // and the exports the whole flow controller of it. type TaskHost struct { - su *Supervisor - // Supervisor with access fields when server is running, i.e restrict access to "Schedule" - // Server that running, is active and open - // Flow controller - FlowController - // Various - pid int - - doneChan chan struct{} - errChan chan error -} - -// Done filled when server was shutdown. -func (h TaskHost) Done() <-chan struct{} { - return h.doneChan -} - -// Err filled when server received an error. -func (h TaskHost) Err() <-chan error { - return h.errChan + Supervisor *Supervisor } // Serve can (re)run the server with the latest known configuration. func (h TaskHost) Serve() error { // the underline server's serve, using the "latest known" listener from the supervisor. - l, err := h.su.newListener() + l, err := h.Supervisor.newListener() if err != nil { return err } // if http.serverclosed ignroe the error, it will have this error // from the previous close - if err := h.su.server.Serve(l); err != http.ErrServerClosed { + if err := h.Supervisor.Server.Serve(l); err != http.ErrServerClosed { return err } return nil @@ -69,12 +69,12 @@ func (h TaskHost) Serve() error { // HostURL returns the listening full url (scheme+host) // based on the supervisor's server's address. func (h TaskHost) HostURL() string { - return nettools.ResolveURLFromServer(h.su.server) + return netutil.ResolveURLFromServer(h.Supervisor.Server) } // Hostname returns the underline server's hostname. func (h TaskHost) Hostname() string { - return nettools.ResolveHostname(h.su.server.Addr) + return netutil.ResolveHostname(h.Supervisor.Server.Addr) } // Shutdown gracefully shuts down the server without interrupting any @@ -88,77 +88,16 @@ func (h TaskHost) Hostname() string { // connections such as WebSockets. The caller of Shutdown should // separately notify such long-lived connections of shutdown and wait // for them to close, if desired. +// +// This Shutdown calls the underline's Server's Shutdown, in order to be able to re-start the server +// from a task. func (h TaskHost) Shutdown(ctx context.Context) error { // the underline server's Shutdown (otherwise we will cancel all tasks and do cycles) - return h.su.server.Shutdown(ctx) -} - -// TaskProcess is the context of the Task runner. -// Contains the host's information and actions -// and its self cancelation emmiter. -type TaskProcess struct { - canceledChan chan struct{} - host TaskHost -} - -// Done filled when this task is canceled. -func (p TaskProcess) Done() <-chan struct{} { - return p.canceledChan -} - -// Host returns the TaskHost. -// -// TaskHost contains all the necessary information -// about the host supervisor, its server -// and the exports the whole flow controller of it. -func (p TaskProcess) Host() TaskHost { - return p.host + return h.Supervisor.Server.Shutdown(ctx) } func createTaskHost(su *Supervisor) TaskHost { - host := TaskHost{ - su: su, - FlowController: su, - doneChan: make(chan struct{}), - errChan: make(chan error), - } - - return host -} - -func newTaskProcess(host TaskHost) TaskProcess { - return TaskProcess{ - host: host, - canceledChan: make(chan struct{}), + return TaskHost{ + Supervisor: su, } } - -// A TaskRunner is an independent stream of instructions in a Supervisor. -// A routine is similar to a sequential program. -// However, a routine itself is not a program, -// it can't run on its own, instead it runs within a Supervisor's context. -// -// The real usage of a routine is not about a single sequential thread, -// but rather using multiple tasks in a single Supervisor. -// Multiple tasks running at the same time and performing various tasks is referred as Multithreading. -// A Task is considered to be a lightweight process because it runs within the context of a Supervisor -// and takes advantage of resources allocated for that Supervisor and its Server. -type TaskRunner interface { - // Run runs the task based on its TaskProcess which contains - // all the necessary information and actions to control the host supervisor - // and its server. - Run(TaskProcess) -} - -// TaskRunnerFunc "converts" a func(TaskProcess) to a complete TaskRunner. -// Its functionality is exactly the same as TaskRunner. -// -// See `TaskRunner` too. -type TaskRunnerFunc func(TaskProcess) - -// Run runs the task based on its TaskProcess which contains -// all the necessary information and actions to control the host supervisor -// and its server. -func (s TaskRunnerFunc) Run(proc TaskProcess) { - s(proc) -} diff --git a/core/host/task_banner.go b/core/host/task_banner.go deleted file mode 100644 index 750760e4..00000000 --- a/core/host/task_banner.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package host - -import ( - "fmt" - "io" - "runtime" -) - -// WriteBannerTask is a task which accepts a logger(io.Writer) -// and a "banner" text to write to following -// by a generated message based on the host supervisor's server and writes it to the "w". -// This task runs on serve. -func WriteBannerTask(w io.Writer, banner string) TaskRunnerFunc { - return func(proc TaskProcess) { - listeningURI := proc.Host().HostURL() - interruptkey := "CTRL" - if runtime.GOOS == "darwin" { - interruptkey = "CMD" - } - w.Write([]byte(fmt.Sprintf("%s\n\nNow listening on: %s\nApplication started. Press %s+C to shut down.\n", - banner, listeningURI, interruptkey))) - } -} diff --git a/core/host/task_example_test.go b/core/host/task_example_test.go deleted file mode 100644 index a8f25065..00000000 --- a/core/host/task_example_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// white-box testing -package host - -import ( - "context" - "fmt" - "net/http" - "time" -) - -func TaskHostError() { - su := New(&http.Server{Addr: ":8273", Handler: http.DefaultServeMux}) - - su.ScheduleFunc(func(proc TaskProcess) { - select { - case err := <-proc.Host().Err(): - fmt.Println(err.Error()) - } - }) - - su.ScheduleFunc(func(proc TaskProcess) { - select { - case err := <-proc.Host().Err(): - fmt.Println(err.Error()) - } - }) - - su.ScheduleFunc(func(proc TaskProcess) { - select { - case err := <-proc.Host().Err(): - fmt.Println(err.Error()) - } - }) - - go su.ListenAndServe() - time.Sleep(1 * time.Second) - su.Shutdown(context.TODO()) - time.Sleep(1 * time.Second) - - // Output: - // http: Server closed - // http: Server closed - // http: Server closed -} diff --git a/core/host/task_interrupt.go b/core/host/task_interrupt.go deleted file mode 100644 index ad96ed93..00000000 --- a/core/host/task_interrupt.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package host - -import ( - "context" - "time" -) - -// OnInterrupt is a built'n supervisor task type which fires its -// value(Task) when an OS interrupt/kill signal received. -type OnInterrupt TaskRunnerFunc - -// Run runs the interrupt task and completes the TaskRunner interface. -func (t OnInterrupt) Run(proc TaskProcess) { - t(proc) -} - -// ShutdownOnInterruptTask returns a supervisor's built'n task which -// shutdowns the server when InterruptSignalTask fire this task. -func ShutdownOnInterruptTask(shutdownTimeout time.Duration) TaskRunner { - return OnInterrupt(func(proc TaskProcess) { - ctx, cancel := context.WithTimeout(context.TODO(), shutdownTimeout) - defer cancel() - proc.Host().Shutdown(ctx) - proc.Host().RestoreFlow() - }) -} diff --git a/core/host/world.go b/core/host/world.go new file mode 100644 index 00000000..2ac1ef1f --- /dev/null +++ b/core/host/world.go @@ -0,0 +1,61 @@ +package host + +import ( + "os" + "os/signal" + "sync" + "syscall" +) + +// package-level interrupt notifier and event firing. + +type world struct { + mu sync.Mutex + // onInterrupt contains a list of the functions that should be called when CTRL+C/CMD+C or + // a unix kill command received. + onInterrupt []func() +} + +var w = &world{} + +// RegisterOnInterrupt registers a global function to call when CTRL+C/CMD+C pressed or a unix kill command received. +func RegisterOnInterrupt(cb func()) { + w.mu.Lock() + w.onInterrupt = append(w.onInterrupt, cb) + w.mu.Unlock() +} + +func notifyInterrupt() { + w.mu.Lock() + for _, f := range w.onInterrupt { + go f() + } + w.mu.Unlock() +} + +func tryStartInterruptNotifier() { + w.mu.Lock() + defer w.mu.Unlock() + if len(w.onInterrupt) > 0 { + // this can't be moved to the task interrupt's `Run` function + // because it will not catch more than one ctrl/cmd+c, so + // we do it here. These tasks are canceled already too. + 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.SIGKILL + os.Kill, + syscall.SIGKILL, // register that too, it should be ok + // kill -SIGTERM XXXX + syscall.SIGTERM, + ) + select { + case <-ch: + notifyInterrupt() + } + }() + } +} diff --git a/core/logger/dev.go b/core/logger/dev.go deleted file mode 100644 index 4e479996..00000000 --- a/core/logger/dev.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package logger - -import ( - "fmt" - "io" - "strings" - "sync" - "time" -) - -// NewDevLogger returns a new logger of io.Writer which -// formats its log message input and writes it -// to the os.Stdout. -func NewDevLogger(omitTimeFor ...string) io.Writer { - mu := &sync.Mutex{} // for now and last log - lastLog := time.Now() - distanceDuration := 850 * time.Millisecond - - return writerFunc(func(p []byte) (int, error) { - logMessage := string(p) - for _, s := range omitTimeFor { - if strings.Contains(logMessage, s) { - n, err := fmt.Print(logMessage) - lastLog = time.Now() - return n, err - } - } - - mu.Lock() - defer mu.Unlock() // "slow" but we don't care here. - nowLog := time.Now() - if nowLog.Before(lastLog.Add(distanceDuration)) { - // don't use the log.Logger to print this message - // if the last one was printed before some seconds. - n, err := fmt.Println(logMessage) // fmt because we don't want the time, dev is dev so console. - lastLog = nowLog - return n, err - } - - // begin with new line in order to have the time once at the top - // and the child logs below it. - n, err := fmt.Printf("%s \u2192\n%s\n", nowLog.Format("01/02/2006 03:04:05"), logMessage) - lastLog = nowLog - return n, err - }) -} diff --git a/core/logger/logger.go b/core/logger/logger.go deleted file mode 100644 index 5e2a4ff0..00000000 --- a/core/logger/logger.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package logger - -import ( - "fmt" - "io" -) - -// writerFunc is just an "extended" io.Writer which provides -// some "methods" which can help applications -// to adapt their existing loggers inside Iris. -type writerFunc func(p []byte) (n int, err error) - -func (w writerFunc) Write(p []byte) (int, error) { - return w(p) -} - -type formatPrintWriter interface { - Printf(string, ...interface{}) -} - -type stringWriter interface { - WriteString(string) (int, error) -} - -// Log sends a message to the defined logger of io.Writer logger, it's -// just a help function for internal use but it can be used to a cusotom middleware too. -// -// See AttachLogger too. -func Log(w io.Writer, format string, a ...interface{}) { - // check if the user's defined logger is one of the "high" - // level printers, if yes then use their functions to print instead - // of allocating new byte slices. - - if fpw, ok := w.(formatPrintWriter); ok { - fpw.Printf(format, a...) - return - } - formattedMessage := fmt.Sprintf(format, a...) - - if sWriter, ok := w.(stringWriter); ok { - sWriter.WriteString(formattedMessage) - return - } - - w.Write([]byte(formattedMessage)) -} diff --git a/core/logger/logger_test.go b/core/logger/logger_test.go deleted file mode 100644 index 6f424cc4..00000000 --- a/core/logger/logger_test.go +++ /dev/null @@ -1,18 +0,0 @@ -// black-box testing -package logger_test - -import ( - "bytes" - "testing" - - "github.com/kataras/iris/core/logger" -) - -func TestLog(t *testing.T) { - msg := "Hello this is me" - l := &bytes.Buffer{} - logger.Log(l, msg) - if expected, got := msg, l.String(); expected != got { - t.Fatalf("expected %s but got %s", expected, got) - } -} diff --git a/core/logger/noop.go b/core/logger/noop.go deleted file mode 100644 index 58333ffa..00000000 --- a/core/logger/noop.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package logger - -// NoOpLogger returns a new, non-operational logger of io.Writer, -// it does nothing any form of input. -var NoOpLogger = writerFunc(func([]byte) (int, error) { return -1, nil }) diff --git a/core/memstore/memstore.go b/core/memstore/memstore.go index 0e61c04c..f5e7e271 100644 --- a/core/memstore/memstore.go +++ b/core/memstore/memstore.go @@ -1,13 +1,6 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - // Package memstore contains a store which is just // a collection of key-value entries with immutability capabilities. // -// Created after that proposal: https://github.com/iris-contrib/community-board/issues/5 -// and it's being used by session entries (session lifetime) and context's entries (per-request). -// // Developers can use that storage to their own apps if they like its behavior. // It's fast and in the same time you get read-only access (safety) when you need it. package memstore diff --git a/core/nettools/addr.go b/core/netutil/addr.go similarity index 91% rename from core/nettools/addr.go rename to core/netutil/addr.go index 0d21033b..66ee0aef 100644 --- a/core/nettools/addr.go +++ b/core/netutil/addr.go @@ -1,8 +1,4 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package nettools +package netutil import ( "os" @@ -199,11 +195,26 @@ func ResolvePort(addr string) int { return 80 } -// ResolveScheme returns the scheme based on the "vhost" -func ResolveScheme(vhost string) string { - // pure check - if strings.HasPrefix(vhost, SchemeHTTPS) || ResolvePort(vhost) == 443 { +// ResolveScheme returns "https://" if "isTLS" receiver is true, +// otherwise "http://". +func ResolveScheme(isTLS bool) string { + if isTLS { return SchemeHTTPS } + return SchemeHTTP } + +// ResolveSchemeFromVHost returns the scheme based on the "vhost". +func ResolveSchemeFromVHost(vhost string) string { + // pure check + isTLS := strings.HasPrefix(vhost, SchemeHTTPS) || ResolvePort(vhost) == 443 + return ResolveScheme(isTLS) +} + +// ResolveURL takes the scheme and an address +// and returns its URL, pure implementation but it does the job. +func ResolveURL(scheme string, addr string) string { + host := ResolveVHost(addr) + return scheme + "://" + host +} diff --git a/core/nettools/addr_test.go b/core/netutil/addr_test.go similarity index 86% rename from core/nettools/addr_test.go rename to core/netutil/addr_test.go index 3b6e2b62..e5fec93d 100644 --- a/core/nettools/addr_test.go +++ b/core/netutil/addr_test.go @@ -1,8 +1,4 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package nettools +package netutil import ( "testing" diff --git a/core/nettools/server.go b/core/netutil/server.go similarity index 76% rename from core/nettools/server.go rename to core/netutil/server.go index 96acdd63..2ad651d3 100644 --- a/core/nettools/server.go +++ b/core/netutil/server.go @@ -1,8 +1,4 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package nettools +package netutil import ( "net/http" @@ -26,11 +22,7 @@ func IsTLS(srv *http.Server) bool { // Returns "https" on secure server, // otherwise "http". func ResolveSchemeFromServer(srv *http.Server) string { - if IsTLS(srv) { - return SchemeHTTPS - } - - return SchemeHTTP + return ResolveScheme(IsTLS(srv)) } // ResolveURLFromServer returns the scheme+host from a server. diff --git a/core/nettools/tcp.go b/core/netutil/tcp.go similarity index 96% rename from core/nettools/tcp.go rename to core/netutil/tcp.go index a68c0ed9..1b025d29 100644 --- a/core/nettools/tcp.go +++ b/core/netutil/tcp.go @@ -1,8 +1,4 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package nettools +package netutil import ( "crypto/tls" diff --git a/core/router/api_builder.go b/core/router/api_builder.go index a4235029..1eaf819b 100644 --- a/core/router/api_builder.go +++ b/core/router/api_builder.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package router import ( @@ -62,13 +58,6 @@ func (r *repository) getAll() []*Route { return r.routes } -// RoutesProvider should be implemented by -// iteral which contains the registered routes. -type RoutesProvider interface { // api builder - GetRoutes() []*Route - GetRoute(routeName string) *Route -} - // APIBuilder the visible API for constructing the router // and child routers. type APIBuilder struct { @@ -81,6 +70,11 @@ type APIBuilder struct { // the api builder global route path reverser object // used by the view engine but it can be used anywhere. reverser *RoutePathReverser + // the api builder global errors, can be filled by the Subdomain, WildcardSubdomain, Handle... + // the list of possible errors that can be + // collected on the build state to log + // to the end-user. + reporter *errors.Reporter // the per-party middleware middleware context.Handlers @@ -101,6 +95,7 @@ func NewAPIBuilder() *APIBuilder { rb := &APIBuilder{ macros: defaultMacros(), errorCodeHandlers: defaultErrorCodeHandlers(), + reporter: errors.NewReporter(), relativePath: "/", routes: new(repository), } @@ -108,17 +103,22 @@ func NewAPIBuilder() *APIBuilder { return rb } +// GetReport returns an error may caused by party's methods. +func (rb *APIBuilder) GetReport() error { + return rb.reporter.Return() +} + // Handle registers a route to the server's rb. // if empty method is passed then handler(s) are being registered to all methods, same as .Any. // -// Returns a *Route and an error which will be filled if route wasn't registered successfully. -func (rb *APIBuilder) Handle(method string, registeredPath string, handlers ...context.Handler) (*Route, error) { +// Returns a *Route, app will throw any errors later on. +func (rb *APIBuilder) Handle(method string, registeredPath string, handlers ...context.Handler) *Route { // if registeredPath[0] != '/' { // return nil, errors.New("path should start with slash and should not be empty") // } if method == "" || method == "ALL" || method == "ANY" { // then use like it was .Any - return nil, rb.Any(registeredPath, handlers...) + return rb.Any(registeredPath, handlers...)[0] } // no clean path yet because of subdomain indicator/separator which contains a dot. @@ -136,31 +136,33 @@ func (rb *APIBuilder) Handle(method string, registeredPath string, handlers ...c routeHandlers := joinHandlers(rb.middleware, handlers) // here we separate the subdomain and relative path - subdomain, path := exctractSubdomain(fullpath) + subdomain, path := splitSubdomainAndPath(fullpath) if len(rb.doneHandlers) > 0 { routeHandlers = append(routeHandlers, rb.doneHandlers...) // register the done middleware, if any } r, err := NewRoute(method, subdomain, path, routeHandlers, rb.macros) - if err != nil { - return nil, err + if err != nil { // template path parser errors: + rb.reporter.Add("%v -> %s:%s:%s", err, method, subdomain, path) + return nil } + // global rb.routes.register(r) - // per -party + // per -party, used for done handlers rb.apiRoutes = append(rb.apiRoutes, r) - // should we remove the rb.apiRoutes on the .Party (new children party) ?, No, because the user maybe use this party later - // should we add to the 'inheritance tree' the rb.apiRoutes, No, these are for this specific party only, because the user propably, will have unexpected behavior when using Use/Use, Done/DoneFunc - return r, nil + + return r } // Party is just a group joiner of routes which have the same prefix and share same middleware(s) also. // Party could also be named as 'Join' or 'Node' or 'Group' , Party chosen because it is fun. func (rb *APIBuilder) Party(relativePath string, handlers ...context.Handler) Party { parentPath := rb.relativePath - dot := string(SubdomainIndicator[0]) - if len(parentPath) > 0 && parentPath[0] == '/' && strings.HasSuffix(relativePath, dot) { // if ends with . , example: admin., it's subdomain-> + dot := string(SubdomainPrefix[0]) + if len(parentPath) > 0 && parentPath[0] == '/' && strings.HasSuffix(relativePath, dot) { + // if ends with . , i.e admin., it's subdomain-> parentPath = parentPath[1:] // remove first slash } @@ -169,6 +171,16 @@ func (rb *APIBuilder) Party(relativePath string, handlers ...context.Handler) Pa parentPath = parentPath[1:] // remove first slash if parent ended with / and new one started with /. } + // if it's subdomain then it has priority, i.e: + // rb.relativePath == "admin." + // relativePath == "panel." + // then it should be panel.admin. + // instead of admin.panel. + if hasSubdomain(parentPath) && hasSubdomain(relativePath) { + relativePath = relativePath + parentPath + parentPath = "" + } + fullpath := parentPath + relativePath // append the parent's +child's handlers middleware := joinHandlers(rb.middleware, handlers) @@ -179,16 +191,46 @@ func (rb *APIBuilder) Party(relativePath string, handlers ...context.Handler) Pa routes: rb.routes, errorCodeHandlers: rb.errorCodeHandlers, doneHandlers: rb.doneHandlers, + reporter: rb.reporter, // per-party/children middleware: middleware, relativePath: fullpath, } } +// Subdomain returns a new party which is responsible to register routes to +// this specific "subdomain". +// +// If called from a child party then the subdomain will be prepended to the path instead of appended. +// So if app.Subdomain("admin.").Subdomain("panel.") then the result is: "panel.admin.". +func (rb *APIBuilder) Subdomain(subdomain string, middleware ...context.Handler) Party { + if rb.relativePath == SubdomainWildcardIndicator { + // cannot concat wildcard subdomain with something else + rb.reporter.Add("cannot concat parent wildcard subdomain with anything else -> %s , %s", + rb.relativePath, subdomain) + return rb + } + return rb.Party(subdomain, middleware...) +} + +// WildcardSubdomain returns a new party which is responsible to register routes to +// a dynamic, wildcard(ed) subdomain. A dynamic subdomain is a subdomain which +// can reply to any subdomain requests. Server will accept any subdomain +// (if not static subdomain found) and it will search and execute the handlers of this party. +func (rb *APIBuilder) WildcardSubdomain(middleware ...context.Handler) Party { + if hasSubdomain(rb.relativePath) { + // cannot concat static subdomain with a dynamic one, wildcard should be at the root level + rb.reporter.Add("cannot concat static subdomain with a dynamic one. Dynamic subdomains should be at the root level -> %s", + rb.relativePath) + return rb + } + return rb.Subdomain(SubdomainWildcardIndicator, middleware...) +} + // Macros returns the macro map which is responsible // to register custom macro functions for all routes. // -// Learn more at: https://github.com/kataras/iris/tree/master/_examples/beginner/routing/dynamic-path +// Learn more at: https://github.com/kataras/iris/tree/master/_examples/routing/dynamic-path func (rb *APIBuilder) Macros() *macro.Map { return rb.macros } @@ -209,8 +251,8 @@ func (rb *APIBuilder) GetRoute(routeName string) *Route { // Use appends Handler(s) to the current Party's routes and child routes. // If the current Party is the root, then it registers the middleware to all child Parties' routes too. -func (rb *APIBuilder) Use(handlers ...context.Handler) { - rb.middleware = append(rb.middleware, handlers...) +func (rb *APIBuilder) Use(middleware ...context.Handler) { + rb.middleware = append(rb.middleware, middleware...) } // Done appends to the very end, Handler(s) to the current Party's routes and child routes @@ -245,83 +287,84 @@ func (rb *APIBuilder) UseGlobal(handlers ...context.Handler) { // Offline(handleResultRouteInfo) // // Returns a *Route and an error which will be filled if route wasn't registered successfully. -func (rb *APIBuilder) None(path string, handlers ...context.Handler) (*Route, error) { +func (rb *APIBuilder) None(path string, handlers ...context.Handler) *Route { return rb.Handle(MethodNone, path, handlers...) } // Get registers a route for the Get http method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. -func (rb *APIBuilder) Get(path string, handlers ...context.Handler) (*Route, error) { +func (rb *APIBuilder) Get(path string, handlers ...context.Handler) *Route { return rb.Handle(http.MethodGet, path, handlers...) } // Post registers a route for the Post http method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. -func (rb *APIBuilder) Post(path string, handlers ...context.Handler) (*Route, error) { +func (rb *APIBuilder) Post(path string, handlers ...context.Handler) *Route { return rb.Handle(http.MethodPost, path, handlers...) } // Put registers a route for the Put http method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. -func (rb *APIBuilder) Put(path string, handlers ...context.Handler) (*Route, error) { +func (rb *APIBuilder) Put(path string, handlers ...context.Handler) *Route { return rb.Handle(http.MethodPut, path, handlers...) } // Delete registers a route for the Delete http method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. -func (rb *APIBuilder) Delete(path string, handlers ...context.Handler) (*Route, error) { +func (rb *APIBuilder) Delete(path string, handlers ...context.Handler) *Route { return rb.Handle(http.MethodDelete, path, handlers...) } // Connect registers a route for the Connect http method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. -func (rb *APIBuilder) Connect(path string, handlers ...context.Handler) (*Route, error) { +func (rb *APIBuilder) Connect(path string, handlers ...context.Handler) *Route { return rb.Handle(http.MethodConnect, path, handlers...) } // Head registers a route for the Head http method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. -func (rb *APIBuilder) Head(path string, handlers ...context.Handler) (*Route, error) { +func (rb *APIBuilder) Head(path string, handlers ...context.Handler) *Route { return rb.Handle(http.MethodHead, path, handlers...) } // Options registers a route for the Options http method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. -func (rb *APIBuilder) Options(path string, handlers ...context.Handler) (*Route, error) { +func (rb *APIBuilder) Options(path string, handlers ...context.Handler) *Route { return rb.Handle(http.MethodOptions, path, handlers...) } // Patch registers a route for the Patch http method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. -func (rb *APIBuilder) Patch(path string, handlers ...context.Handler) (*Route, error) { +func (rb *APIBuilder) Patch(path string, handlers ...context.Handler) *Route { return rb.Handle(http.MethodPatch, path, handlers...) } // Trace registers a route for the Trace http method. // // Returns a *Route and an error which will be filled if route wasn't registered successfully. -func (rb *APIBuilder) Trace(path string, handlers ...context.Handler) (*Route, error) { +func (rb *APIBuilder) Trace(path string, handlers ...context.Handler) *Route { return rb.Handle(http.MethodTrace, path, handlers...) } // Any registers a route for ALL of the http methods // (Get,Post,Put,Head,Patch,Options,Connect,Delete). -func (rb *APIBuilder) Any(registeredPath string, handlers ...context.Handler) error { - for _, k := range AllMethods { - if _, err := rb.Handle(k, registeredPath, handlers...); err != nil { - return err - } +func (rb *APIBuilder) Any(registeredPath string, handlers ...context.Handler) []*Route { + routes := make([]*Route, len(AllMethods), len(AllMethods)) + + for i, k := range AllMethods { + r := rb.Handle(k, registeredPath, handlers...) + routes[i] = r } - return nil + return routes } // StaticCacheDuration expiration duration for INACTIVE file handlers, it's the only one global configuration @@ -341,10 +384,8 @@ const ( varyHeaderKey = "Vary" ) -func (rb *APIBuilder) registerResourceRoute(reqPath string, h context.Handler) (*Route, error) { - if _, err := rb.Head(reqPath, h); err != nil { - return nil, err - } +func (rb *APIBuilder) registerResourceRoute(reqPath string, h context.Handler) *Route { + rb.Head(reqPath, h) return rb.Get(reqPath, h) } @@ -365,7 +406,7 @@ func (rb *APIBuilder) registerResourceRoute(reqPath string, h context.Handler) ( // mySubdomainFsServer.Get("/static", h) // ... // -func (rb *APIBuilder) StaticHandler(systemPath string, showList bool, enableGzip bool, exceptRoutes ...*Route) context.Handler { +func (rb *APIBuilder) StaticHandler(systemPath string, showList bool, enableGzip bool) context.Handler { // Note: this doesn't need to be here but we'll keep it for consistently return StaticHandler(systemPath, showList, enableGzip) } @@ -379,7 +420,7 @@ func (rb *APIBuilder) StaticHandler(systemPath string, showList bool, enableGzip // it uses gzip compression (compression on each request, no file cache). // // Returns the GET *Route. -func (rb *APIBuilder) StaticServe(systemPath string, requestPath ...string) (*Route, error) { +func (rb *APIBuilder) StaticServe(systemPath string, requestPath ...string) *Route { var reqPath string if len(requestPath) == 0 { @@ -411,12 +452,13 @@ func (rb *APIBuilder) StaticServe(systemPath string, requestPath ...string) (*Ro // that are ready to serve raw static bytes, memory cached. // // Returns the GET *Route. -func (rb *APIBuilder) StaticContent(reqPath string, cType string, content []byte) (*Route, error) { +func (rb *APIBuilder) StaticContent(reqPath string, cType string, content []byte) *Route { modtime := time.Now() h := func(ctx context.Context) { - if err := ctx.WriteWithExpiration(content, cType, modtime); err != nil { - ctx.NotFound() - // ctx.Application().Log("error while serving []byte via StaticContent: %s", err.Error()) + ctx.ContentType(cType) + if _, err := ctx.WriteWithExpiration(content, modtime); err != nil { + ctx.StatusCode(http.StatusInternalServerError) + // ctx.Application().Logger().Infof("error while serving []byte via StaticContent: %s", err.Error()) } } @@ -444,7 +486,7 @@ func (rb *APIBuilder) StaticEmbeddedHandler(vdir string, assetFn func(name strin // Returns the GET *Route. // // Examples: https://github.com/kataras/iris/tree/master/_examples/file-server -func (rb *APIBuilder) StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string) (*Route, error) { +func (rb *APIBuilder) StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route { fullpath := joinPath(rb.relativePath, requestPath) requestPath = joinPath(fullpath, WildcardParam("file")) @@ -466,12 +508,13 @@ var errDirectoryFileNotFound = errors.New("Directory or file %s couldn't found. // Note that you have to call it on every favicon you have to serve automatically (desktop, mobile and so on). // // Returns the GET *Route. -func (rb *APIBuilder) Favicon(favPath string, requestPath ...string) (*Route, error) { +func (rb *APIBuilder) Favicon(favPath string, requestPath ...string) *Route { favPath = Abs(favPath) f, err := os.Open(favPath) if err != nil { - return nil, errDirectoryFileNotFound.Format(favPath, err.Error()) + rb.reporter.AddErr(errDirectoryFileNotFound.Format(favPath, err.Error())) + return nil } // ignore error f.Close() @@ -496,8 +539,9 @@ func (rb *APIBuilder) Favicon(favPath string, requestPath ...string) (*Route, er // So we could panic but we don't, // we just interrupt with a message // to the (user-defined) logger. - return nil, errDirectoryFileNotFound. - Format(favPath, "favicon: couldn't read the data bytes for file: "+err.Error()) + rb.reporter.AddErr(errDirectoryFileNotFound. + Format(favPath, "favicon: couldn't read the data bytes for file: "+err.Error())) + return nil } modtime := "" h := func(ctx context.Context) { @@ -516,7 +560,7 @@ func (rb *APIBuilder) Favicon(favPath string, requestPath ...string) (*Route, er ctx.ResponseWriter().Header().Set(lastModifiedHeaderKey, modtime) ctx.StatusCode(http.StatusOK) if _, err := ctx.Write(cacheFav); err != nil { - // ctx.Application().Log("error while trying to serve the favicon: %s", err.Error()) + // ctx.Application().Logger().Infof("error while trying to serve the favicon: %s", err.Error()) ctx.StatusCode(http.StatusInternalServerError) } } @@ -534,9 +578,8 @@ func (rb *APIBuilder) Favicon(favPath string, requestPath ...string) (*Route, er // // first parameter: the route path // second parameter: the system directory -// third OPTIONAL parameter: the exception routes -// (= give priority to these routes instead of the static handler) -// for more options look rb.StaticHandler. +// +// for more options look router.StaticHandler. // // rb.StaticWeb("/static", "./static") // @@ -547,13 +590,13 @@ func (rb *APIBuilder) Favicon(favPath string, requestPath ...string) (*Route, er // StaticWeb calls the StaticHandler(systemPath, listingDirectories: false, gzip: false ). // // Returns the GET *Route. -func (rb *APIBuilder) StaticWeb(requestPath string, systemPath string, exceptRoutes ...*Route) (*Route, error) { +func (rb *APIBuilder) StaticWeb(requestPath string, systemPath string) *Route { paramName := "file" fullpath := joinPath(rb.relativePath, requestPath) - h := StripPrefix(fullpath, rb.StaticHandler(systemPath, false, false, exceptRoutes...)) + h := StripPrefix(fullpath, rb.StaticHandler(systemPath, false, false)) handler := func(ctx context.Context) { h(ctx) diff --git a/core/router/fs.go b/core/router/fs.go index 59b459da..6a2e8345 100644 --- a/core/router/fs.go +++ b/core/router/fs.go @@ -1,10 +1,7 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ & 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. - package router import ( + "errors" "fmt" "io" "mime/multipart" @@ -21,7 +18,6 @@ import ( "time" "github.com/kataras/iris/context" - "github.com/kataras/iris/core/errors" ) // StaticEmbeddedHandler returns a Handler which can serve @@ -86,8 +82,8 @@ func StaticEmbeddedHandler(vdir string, assetFn func(name string) ([]byte, error if err != nil { continue } - - if err := ctx.WriteWithExpiration(buf, cType, modtime); err != nil { + ctx.ContentType(cType) + if _, err := ctx.WriteWithExpiration(buf, modtime); err != nil { ctx.StatusCode(http.StatusInternalServerError) ctx.StopExecution() } @@ -102,36 +98,6 @@ func StaticEmbeddedHandler(vdir string, assetFn func(name string) ([]byte, error return h } -// Prioritize is a middleware which executes a route against this path -// when the request's Path has a prefix of the route's STATIC PART -// is not executing ExecRoute to determinate if it's valid, for performance reasons -// if this function is not enough for you and you want to test more than one parameterized path -// then use the: if c := ExecRoute(r); c == nil { /* move to the next, the route is not valid */ } -// -// You can find the Route by iris.Default.Routes().Lookup("theRouteName") -// you can set a route name as: myRoute := iris.Default.Get("/mypath", handler)("theRouteName") -// that will set a name to the route and returns its iris.Route instance for further usage. -// -// if the route found then it executes that and don't continue to the next handler -// if not found then continue to the next handler -func Prioritize(r *Route) context.Handler { - if r != nil { - return func(ctx context.Context) { - reqPath := ctx.Path() - staticPath := ResolveStaticPath(reqPath) - if strings.HasPrefix(reqPath, staticPath) { - ctx.Exec(r.Method, reqPath) // execute the route based on this request path - // we are done here. - return - } - // execute the next handler if no prefix - // here look, the only error we catch is the 404. - ctx.Next() - } - } - return func(ctx context.Context) { ctx.Next() } -} - // StaticHandler returns a new Handler which is ready // to serve all kind of static files. // @@ -148,20 +114,18 @@ func Prioritize(r *Route) context.Handler { // app.Get("/static", h) // ... // -func StaticHandler(systemPath string, showList bool, enableGzip bool, exceptRoutes ...*Route) context.Handler { +func StaticHandler(systemPath string, showList bool, enableGzip bool) context.Handler { return NewStaticHandlerBuilder(systemPath). Listing(showList). Gzip(enableGzip). - Except(exceptRoutes...). Build() } // StaticHandlerBuilder is the web file system's Handler builder -// use that or the iris.StaticHandler/StaticWeb methods +// use that or the iris.StaticHandler/StaticWeb methods. type StaticHandlerBuilder interface { Gzip(enable bool) StaticHandlerBuilder Listing(listDirectoriesOnOff bool) StaticHandlerBuilder - Except(r ...*Route) StaticHandlerBuilder Build() context.Handler } @@ -179,7 +143,6 @@ type fsHandler struct { // these are init on the Build() call filesystem http.FileSystem once sync.Once - exceptions []*Route handler context.Handler } @@ -235,13 +198,6 @@ func (w *fsHandler) Listing(listDirectoriesOnOff bool) StaticHandlerBuilder { return w } -// Except add a route exception, -// gives priority to that Route over the static handler. -func (w *fsHandler) Except(r ...*Route) StaticHandlerBuilder { - w.exceptions = append(w.exceptions, r...) - return w -} - type ( noListFile struct { http.File @@ -308,7 +264,7 @@ func (w *fsHandler) Build() context.Handler { // headers[contentEncodingHeader] = nil // headers[contentLength] = nil } - // ctx.Application().Log(errMsg) + // ctx.Application().Logger().Infof(errMsg) ctx.StatusCode(prevStatusCode) return } @@ -317,21 +273,6 @@ func (w *fsHandler) Build() context.Handler { ctx.Next() } - if len(w.exceptions) > 0 { - middleware := make(context.Handlers, len(w.exceptions)+1) - for i := range w.exceptions { - middleware[i] = Prioritize(w.exceptions[i]) - } - middleware[len(w.exceptions)] = fileserver - - w.handler = func(ctx context.Context) { - ctxHandlers := ctx.Handlers() - ctx.SetHandlers(append(middleware, ctxHandlers...)) - ctx.Handlers()[0](ctx) - } - return - } - w.handler = fileserver }) @@ -357,7 +298,7 @@ func StripPrefix(prefix string, h context.Handler) context.Handler { // here we separate the path from the subdomain (if any), we care only for the path // fixes a bug when serving static files via a subdomain fixedPrefix := prefix - if dotWSlashIdx := strings.Index(fixedPrefix, SubdomainIndicator); dotWSlashIdx > 0 { + if dotWSlashIdx := strings.Index(fixedPrefix, SubdomainPrefix); dotWSlashIdx > 0 { fixedPrefix = fixedPrefix[dotWSlashIdx+1:] } fixedPrefix = toWebPath(fixedPrefix) diff --git a/core/router/handler.go b/core/router/handler.go index 2f0570e8..089069a0 100644 --- a/core/router/handler.go +++ b/core/router/handler.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package router import ( @@ -11,7 +7,9 @@ import ( "strings" "github.com/kataras/iris/context" - "github.com/kataras/iris/core/nettools" + + "github.com/kataras/iris/core/errors" + "github.com/kataras/iris/core/netutil" "github.com/kataras/iris/core/router/node" ) @@ -70,6 +68,13 @@ func NewDefaultHandler() RequestHandler { return h } +// RoutesProvider should be implemented by +// iteral which contains the registered routes. +type RoutesProvider interface { // api builder + GetRoutes() []*Route + GetRoute(routeName string) *Route +} + func (h *routerHandler) Build(provider RoutesProvider) error { registeredRoutes := provider.GetRoutes() h.trees = h.trees[0:0] // reset, inneed when rebuilding. @@ -79,20 +84,24 @@ func (h *routerHandler) Build(provider RoutesProvider) error { return len(registeredRoutes[i].Subdomain) >= len(registeredRoutes[j].Subdomain) }) + rp := errors.NewReporter() + for _, r := range registeredRoutes { if r.Subdomain != "" { h.hosts = true } + // the only "bad" with this is if the user made an error // on route, it will be stacked shown in this build state // and no in the lines of the user's action, they should read // the docs better. Or TODO: add a link here in order to help new users. if err := h.addRoute(r.Method, r.Subdomain, r.Path, r.Handlers); err != nil { - return err + // node errors: + rp.Add("%v -> %s", err, r.String()) } } - return nil + return rp.Return() } func (h *routerHandler) HandleRequest(ctx context.Context) { @@ -134,14 +143,14 @@ func (h *routerHandler) HandleRequest(ctx context.Context) { if h.hosts && t.Subdomain != "" { requestHost := ctx.Host() - if nettools.IsLoopbackSubdomain(requestHost) { + if netutil.IsLoopbackSubdomain(requestHost) { // this fixes a bug when listening on // 127.0.0.1:8080 for example // and have a wildcard subdomain and a route registered to root domain. continue // it's not a subdomain, it's something like 127.0.0.1 probably } // it's a dynamic wildcard subdomain, we have just to check if ctx.subdomain is not empty - if t.Subdomain == DynamicSubdomainIndicator { + if t.Subdomain == SubdomainWildcardIndicator { // mydomain.com -> invalid // localhost -> invalid // sub.mydomain.com -> valid diff --git a/core/router/macro.go b/core/router/macro.go index 001d4b90..8deedd17 100644 --- a/core/router/macro.go +++ b/core/router/macro.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package router import ( diff --git a/core/router/macro/LICENSE b/core/router/macro/LICENSE deleted file mode 100644 index 7eb6b6e4..00000000 --- a/core/router/macro/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. 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 Gerasimos Maropoulos nor the name of his -username, kataras, 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. \ No newline at end of file diff --git a/core/router/macro/interpreter/ast/ast.go b/core/router/macro/interpreter/ast/ast.go index 0d36aeec..299946a8 100644 --- a/core/router/macro/interpreter/ast/ast.go +++ b/core/router/macro/interpreter/ast/ast.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package ast import ( diff --git a/core/router/macro/interpreter/lexer/lexer.go b/core/router/macro/interpreter/lexer/lexer.go index 7af0f085..79f7111f 100644 --- a/core/router/macro/interpreter/lexer/lexer.go +++ b/core/router/macro/interpreter/lexer/lexer.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package lexer import ( diff --git a/core/router/macro/interpreter/lexer/lexer_test.go b/core/router/macro/interpreter/lexer/lexer_test.go index 39aa709c..dad919f3 100644 --- a/core/router/macro/interpreter/lexer/lexer_test.go +++ b/core/router/macro/interpreter/lexer/lexer_test.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package lexer import ( diff --git a/core/router/macro/interpreter/parser/parser.go b/core/router/macro/interpreter/parser/parser.go index 7a785c18..8a352a73 100644 --- a/core/router/macro/interpreter/parser/parser.go +++ b/core/router/macro/interpreter/parser/parser.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package parser import ( diff --git a/core/router/macro/interpreter/parser/parser_test.go b/core/router/macro/interpreter/parser/parser_test.go index c30d714d..caec39dc 100644 --- a/core/router/macro/interpreter/parser/parser_test.go +++ b/core/router/macro/interpreter/parser/parser_test.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package parser import ( diff --git a/core/router/macro/interpreter/token/token.go b/core/router/macro/interpreter/token/token.go index 29c6b3f5..620ad641 100644 --- a/core/router/macro/interpreter/token/token.go +++ b/core/router/macro/interpreter/token/token.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package token // Type is a specific type of int which describes the symbols. diff --git a/core/router/macro/macro.go b/core/router/macro/macro.go index 4b57681c..719d759f 100644 --- a/core/router/macro/macro.go +++ b/core/router/macro/macro.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package macro import ( @@ -239,7 +235,7 @@ type Map struct { // NewMap returns a new macro Map with default // type evaluators. // -// Learn more at: https://github.com/kataras/iris/tree/master/_examples/beginner/routing/dynamic-path +// Learn more at: https://github.com/kataras/iris/tree/master/_examples/routing/dynamic-path func NewMap() *Map { return &Map{ // it allows everything, so no need for a regexp here. diff --git a/core/router/macro/macro_test.go b/core/router/macro/macro_test.go index f103684e..d412da29 100644 --- a/core/router/macro/macro_test.go +++ b/core/router/macro/macro_test.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package macro import ( @@ -179,7 +175,7 @@ func TestPathEvaluatorRaw(t *testing.T) { // } // }) -// p, err := Parse("/user/@kataras") +// p, err := Parse("/user/@iris") // if err != nil { // t.Fatalf(err) // } diff --git a/core/router/macro/template.go b/core/router/macro/template.go index dbaa1bba..b1a3afa3 100644 --- a/core/router/macro/template.go +++ b/core/router/macro/template.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package macro import ( diff --git a/core/router/node/node.go b/core/router/node/node.go index e3ee3b4d..29032e34 100644 --- a/core/router/node/node.go +++ b/core/router/node/node.go @@ -20,8 +20,8 @@ type node struct { root bool } -// ErrDublicate returned from MakeChild when more than one routes have the same registered path. -var ErrDublicate = errors.New("more than one routes have the same registered path") +// ErrDublicate returnned from `Add` when two or more routes have the same registered path. +var ErrDublicate = errors.New("two or more routes have the same registered path") // Add adds a node to the tree, returns an ErrDublicate error on failure. func (nodes *Nodes) Add(path string, handlers context.Handlers) error { @@ -48,12 +48,22 @@ func (nodes *Nodes) Add(path string, handlers context.Handlers) error { paramEnd -= paramEnd - paramStart } - for _, idx := range paramsPos(path) { + var p []int + for i := 0; i < len(path); i++ { + idx := strings.IndexByte(path[i:], ':') + if idx == -1 { + break + } + p = append(p, idx+i) + i = idx + i + } - if err := nodes.add(path[:idx], nil, nil, true); err != nil { // take the static path to its own node + for _, idx := range p { + + if err := nodes.add(path[:idx], nil, nil, true); err != nil { return err } - // create a second, empty, dynamic parameter node without the last slash + if nidx := idx + 1; len(path) > nidx { if err := nodes.add(path[:nidx], nil, nil, true); err != nil { return err @@ -61,13 +71,12 @@ func (nodes *Nodes) Add(path string, handlers context.Handlers) error { } } - // last, create the node filled by the full path, parameters and its handlers if err := nodes.add(path, params, handlers, true); err != nil { return err } - // sort by static path, remember, they were already sorted by subdomains too. - nodes.Sort() + // prioritize by static path remember, they were already sorted by subdomains too. + nodes.prioritize() return nil } @@ -153,7 +162,7 @@ loop: return nil } if len(n.handlers) > 0 { // n.handlers already setted - return ErrDublicate.Append("for: %s", n.s) + return ErrDublicate } n.paramNames = paramNames n.handlers = handlers @@ -216,7 +225,7 @@ func (nodes Nodes) findChild(path string, params []string) (*node, []string) { continue } - if len(path) == len(n.s) { // Node matched until the end of path. + if len(path) == len(n.s) { if len(n.handlers) == 0 { return nil, nil } @@ -226,7 +235,7 @@ func (nodes Nodes) findChild(path string, params []string) (*node, []string) { child, childParamNames := n.children.findChild(path[len(n.s):], params) if child == nil || len(child.handlers) == 0 { - // is wildcard and it is not root neither has children + if n.s[len(n.s)-1] == '/' && !(n.root && (n.s == "/" || len(n.children) > 0)) { if len(n.handlers) == 0 { return nil, nil @@ -254,8 +263,8 @@ func (n *node) isDynamic() bool { return n.s == ":" } -// Sort sets the static paths first. -func (nodes Nodes) Sort() { +// prioritize sets the static paths first. +func (nodes Nodes) prioritize() { sort.Slice(nodes, func(i, j int) bool { if nodes[i].isDynamic() { @@ -268,18 +277,6 @@ func (nodes Nodes) Sort() { }) for _, n := range nodes { - n.children.Sort() + n.children.prioritize() } } - -func paramsPos(s string) (pos []int) { - for i := 0; i < len(s); i++ { - p := strings.IndexByte(s[i:], ':') - if p == -1 { - break - } - pos = append(pos, p+i) - i = p + i - } - return -} diff --git a/core/router/party.go b/core/router/party.go index f034bc92..44975a85 100644 --- a/core/router/party.go +++ b/core/router/party.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package router import ( @@ -15,11 +11,17 @@ import ( // Look the "APIBuilder" for its implementation. type Party interface { // Party creates and returns a new child Party with the following features. - Party(relativePath string, handlers ...context.Handler) Party + Party(relativePath string, middleware ...context.Handler) Party + // Subdomain returns a new party which is responsible to register routes to + // this specific "subdomain". + // + // If called from a child party then the subdomain will be prepended to the path instead of appended. + // So if app.Subdomain("admin.").Subdomain("panel.") then the result is: "panel.admin.". + Subdomain(subdomain string, middleware ...context.Handler) Party // Use appends Handler(s) to the current Party's routes and child routes. // If the current Party is the root, then it registers the middleware to all child Parties' routes too. - Use(handlers ...context.Handler) + Use(middleware ...context.Handler) // Done appends to the very end, Handler(s) to the current Party's routes and child routes // The difference from .Use is that this/or these Handler(s) are being always running last. @@ -29,7 +31,7 @@ type Party interface { // if empty method is passed then handler(s) are being registered to all methods, same as .Any. // // Returns the read-only route information. - Handle(method string, registeredPath string, handlers ...context.Handler) (*Route, error) + Handle(method string, registeredPath string, handlers ...context.Handler) *Route // None registers an "offline" route // see context.ExecRoute(routeName) and @@ -37,47 +39,47 @@ type Party interface { // Offline(handleResultregistry.*Route) // // Returns the read-only route information. - None(path string, handlers ...context.Handler) (*Route, error) + None(path string, handlers ...context.Handler) *Route // Get registers a route for the Get http method. // // Returns the read-only route information. - Get(path string, handlers ...context.Handler) (*Route, error) + Get(path string, handlers ...context.Handler) *Route // Post registers a route for the Post http method. // // Returns the read-only route information. - Post(path string, handlers ...context.Handler) (*Route, error) + Post(path string, handlers ...context.Handler) *Route // Put registers a route for the Put http method. // // Returns the read-only route information. - Put(path string, handlers ...context.Handler) (*Route, error) + Put(path string, handlers ...context.Handler) *Route // Delete registers a route for the Delete http method. // // Returns the read-only route information. - Delete(path string, handlers ...context.Handler) (*Route, error) + Delete(path string, handlers ...context.Handler) *Route // Connect registers a route for the Connect http method. // // Returns the read-only route information. - Connect(path string, handlers ...context.Handler) (*Route, error) + Connect(path string, handlers ...context.Handler) *Route // Head registers a route for the Head http method. // // Returns the read-only route information. - Head(path string, handlers ...context.Handler) (*Route, error) + Head(path string, handlers ...context.Handler) *Route // Options registers a route for the Options http method. // // Returns the read-only route information. - Options(path string, handlers ...context.Handler) (*Route, error) + Options(path string, handlers ...context.Handler) *Route // Patch registers a route for the Patch http method. // // Returns the read-only route information. - Patch(path string, handlers ...context.Handler) (*Route, error) + Patch(path string, handlers ...context.Handler) *Route // Trace registers a route for the Trace http method. // // Returns the read-only route information. - Trace(path string, handlers ...context.Handler) (*Route, error) + Trace(path string, handlers ...context.Handler) *Route // Any registers a route for ALL of the http methods // (Get,Post,Put,Head,Patch,Options,Connect,Delete). - Any(registeredPath string, handlers ...context.Handler) error + Any(registeredPath string, handlers ...context.Handler) []*Route // StaticHandler returns a new Handler which is ready // to serve all kind of static files. @@ -96,7 +98,7 @@ type Party interface { // mySubdomainFsServer.Get("/static", h) // ... // - StaticHandler(systemPath string, showList bool, enableGzip bool, exceptRoutes ...*Route) context.Handler + StaticHandler(systemPath string, showList bool, enableGzip bool) context.Handler // StaticServe serves a directory as web resource // it's the simpliest form of the Static* functions @@ -107,12 +109,12 @@ type Party interface { // it uses gzip compression (compression on each request, no file cache). // // Returns the GET *Route. - StaticServe(systemPath string, requestPath ...string) (*Route, error) + StaticServe(systemPath string, requestPath ...string) *Route // StaticContent registers a GET and HEAD method routes to the requestPath // that are ready to serve raw static bytes, memory cached. // // Returns the GET *Route. - StaticContent(requestPath string, cType string, content []byte) (*Route, error) + StaticContent(requestPath string, cType string, content []byte) *Route // StaticEmbedded used when files are distributed inside the app executable, using go-bindata mostly // First parameter is the request path, the path which the files in the vdir will be served to, for example "/static" @@ -122,8 +124,8 @@ type Party interface { // // Returns the GET *Route. // - // Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/serve-embedded-files - StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string) (*Route, error) + // Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-files-into-app + StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string) *Route // Favicon serves static favicon // accepts 2 parameters, second is optional @@ -136,14 +138,13 @@ type Party interface { // Note that you have to call it on every favicon you have to serve automatically (desktop, mobile and so on). // // Returns the GET *Route. - Favicon(favPath string, requestPath ...string) (*Route, error) + Favicon(favPath string, requestPath ...string) *Route // StaticWeb returns a handler that serves HTTP requests // with the contents of the file system rooted at directory. // // first parameter: the route path // second parameter: the system directory - // third OPTIONAL parameter: the exception routes - // (= give priority to these routes instead of the static handler) + // // for more options look router.StaticHandler. // // router.StaticWeb("/static", "./static") @@ -155,7 +156,7 @@ type Party interface { // StaticWeb calls the StaticHandler(systemPath, listingDirectories: false, gzip: false ). // // Returns the GET *Route. - StaticWeb(requestPath string, systemPath string, exceptRoutes ...*Route) (*Route, error) + StaticWeb(requestPath string, systemPath string) *Route // Layout oerrides the parent template layout with a more specific layout for this Party // returns this Party, to continue as normal diff --git a/core/router/path.go b/core/router/path.go index d0abcefd..732e944c 100644 --- a/core/router/path.go +++ b/core/router/path.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package router import ( @@ -10,8 +6,7 @@ import ( "strconv" "strings" - "github.com/esemplastic/unis" - "github.com/kataras/iris/core/nettools" + "github.com/kataras/iris/core/netutil" ) const ( @@ -22,18 +17,6 @@ const ( WildcardParamStart = "*" ) -// ResolveStaticPath receives a (dynamic) path and tries to return its static path part. -func ResolveStaticPath(original string) string { - i := strings.Index(original, ParamStart) - v := strings.Index(original, WildcardParamStart) - - return unis.NewChain( - unis.NewConditional( - unis.NewRangeEnd(i), - unis.NewRangeEnd(v)), - cleanPath).Process(original) -} - // Param receives a parameter name prefixed with the ParamStart symbol. func Param(name string) string { return prefix(name, ParamStart) @@ -47,8 +30,19 @@ func WildcardParam(name string) string { return prefix(name, WildcardParamStart) } -func prefix(str string, prefix string) string { - return unis.NewExclusivePrepender(prefix).Process(str) +func prefix(s string, prefix string) string { + if !strings.HasPrefix(s, prefix) { + return prefix + s + } + + return s +} + +func suffix(s string, suffix string) string { + if !strings.HasSuffix(s, suffix) { + return s + suffix + } + return s } func joinPath(path1 string, path2 string) string { @@ -65,59 +59,78 @@ func joinPath(path1 string, path2 string) string { // that is, replace "/.." by "/" at the beginning of a path. // // The returned path ends in a slash only if it is the root "/". -var cleanPath = unis.NewChain( - - unis.NewSuffixRemover("/"), - unis.NewTargetedJoiner(0, '/'), - unis.ProcessorFunc(path.Clean), - unis.NewReplacer(map[string]string{ - "//": "/", - "\\": "/", - }), - unis.ProcessorFunc(func(s string) string { - if s == "" || s == "." { - return "/" - } - return s - }), -) - -const ( - // DynamicSubdomainIndicator where a registered path starts with '*.' then it contains a dynamic subdomain, if subdomain == "*." then its dynamic - // - // used internally by URLPath and the router serve. - DynamicSubdomainIndicator = "*." - // SubdomainIndicator where './' exists in a registered path then it contains subdomain - // - // used on router builder - SubdomainIndicator = "./" -) - -func newSubdomainDivider(sep string) unis.DividerFunc { - // invert if indiciator not found - // because we need the first parameter to be the subdomain - // even if empty, but the second parameter - // should be the path, in order to normalize it - // (because of the reason of subdomains shouldn't be normalized as path) - subdomainDevider := unis.NewInvertOnFailureDivider(unis.NewDivider(sep)) - return func(fullpath string) (string, string) { - subdomain, path := subdomainDevider.Divide(fullpath) - if len(path) > 1 { - if path[0] == '/' && path[1] == '/' { - path = path[1:] - } - } - - return subdomain, path //cleanPath(path) +func cleanPath(s string) string { + if s == "" || s == "." { + return "/" } + + // remove suffix "/" + if lidx := len(s) - 1; s[lidx] == '/' { + s = s[:lidx] + } + + // prefix with "/" + s = prefix(s, "/") + + // remove the os specific dir sep + s = strings.Replace(s, "\\", "/", -1) + + // use std path to clean the path + s = path.Clean(s) + + return s } -// ExctractSubdomain checks if the path has subdomain and if it's +const ( + // SubdomainWildcardIndicator where a registered path starts with '*.'. + // if subdomain == "*." then its wildcard. + // + // used internally by router and api builder. + SubdomainWildcardIndicator = "*." + + // SubdomainWildcardPrefix where a registered path starts with "*./", + // then this route should accept any subdomain. + SubdomainWildcardPrefix = SubdomainWildcardIndicator + "/" + // SubdomainPrefix where './' exists in a registered path then it contains subdomain + // + // used on api builder. + SubdomainPrefix = "./" // i.e subdomain./ -> Subdomain: subdomain. Path: / +) + +func hasSubdomain(s string) bool { + if s == "" { + return false + } + + // subdomain./path + // .*/path + // + // remember: path always starts with "/" + // if not start with "/" then it should be something else, + // we don't assume anything else but subdomain. + slashIdx := strings.IndexByte(s, '/') + return slashIdx > 0 || s[0] == SubdomainPrefix[0] || (len(s) >= 2 && s[0:2] == SubdomainWildcardIndicator) +} + +// splitSubdomainAndPath checks if the path has subdomain and if it's // it splits the subdomain and path and returns them, otherwise it returns -// an empty subdomain and the given path. +// an empty subdomain and the clean path. // // First return value is the subdomain, second is the path. -var exctractSubdomain = newSubdomainDivider(SubdomainIndicator) +func splitSubdomainAndPath(fullUnparsedPath string) (subdomain string, path string) { + s := fullUnparsedPath + if s == "" || s == "/" { + return "", "/" + } + + slashIdx := strings.IndexByte(s, '/') + if slashIdx == 0 { + // no subdomain + return "", cleanPath(s) + } + + return s[0:slashIdx], cleanPath(s[slashIdx:]) // return subdomain without slash, path with slash +} // RoutePathReverserOption option signature for the RoutePathReverser. type RoutePathReverserOption func(*RoutePathReverser) @@ -142,7 +155,7 @@ func WithHost(host string) RoutePathReverserOption { return func(ps *RoutePathReverser) { ps.vhost = host if ps.vscheme == "" { - ps.vscheme = nettools.ResolveScheme(host) + ps.vscheme = netutil.ResolveSchemeFromVHost(host) } } } @@ -152,8 +165,8 @@ func WithHost(host string) RoutePathReverserOption { // a scheme and a host to be used in the URL function. func WithServer(srv *http.Server) RoutePathReverserOption { return func(ps *RoutePathReverser) { - ps.vhost = nettools.ResolveVHost(srv.Addr) - ps.vscheme = nettools.ResolveSchemeFromServer(srv) + ps.vhost = netutil.ResolveVHost(srv.Addr) + ps.vscheme = netutil.ResolveSchemeFromServer(srv) } } @@ -222,7 +235,7 @@ func toStringSlice(args []interface{}) []string { // Remove the URL for now, it complicates things for the whole framework without a specific benefits, // developers can just concat the subdomain, (host can be auto-retrieve by browser using the Path). -// URL same as Path but returns the full uri, i.e https://mysubdomain.mydomain.com/hello/kataras +// URL same as Path but returns the full uri, i.e https://mysubdomain.mydomain.com/hello/iris func (ps *RoutePathReverser) URL(routeName string, paramValues ...interface{}) (url string) { if ps.vhost == "" || ps.vscheme == "" { return "not supported" @@ -243,7 +256,7 @@ func (ps *RoutePathReverser) URL(routeName string, paramValues ...interface{}) ( scheme := ps.vscheme // if it's dynamic subdomain then the first argument is the subdomain part // for this part we are responsible not the custom routers - if r.Subdomain == DynamicSubdomainIndicator { + if r.Subdomain == SubdomainWildcardIndicator { subdomain := args[0] host = subdomain + "." + host args = args[1:] // remove the subdomain part for the arguments, diff --git a/core/router/path_test.go b/core/router/path_test.go new file mode 100644 index 00000000..31194871 --- /dev/null +++ b/core/router/path_test.go @@ -0,0 +1,29 @@ +package router + +import ( + "testing" +) + +func TestSplitSubdomainAndPath(t *testing.T) { + tests := []struct { + original string + subdomain string + path string + }{ + {"admin./users/42", "admin.", "/users/42"}, + {"//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 8c1df46c..c22b2490 100644 --- a/core/router/route.go +++ b/core/router/route.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package router import ( @@ -61,6 +57,12 @@ func NewRoute(method, subdomain, unparsedPath string, return route, nil } +// String returns the form of METHOD, SUBDOMAIN, TMPL PATH +func (r Route) String() string { + return fmt.Sprintf("%s %s%s", + r.Method, r.Subdomain, r.Tmpl().Src) +} + // Tmpl returns the path template, i // it contains the parsed template // for the route's path. diff --git a/core/router/router.go b/core/router/router.go index 9994a294..05c5af11 100644 --- a/core/router/router.go +++ b/core/router/router.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package router import ( diff --git a/core/router/spa.go b/core/router/router_spa_wrapper.go similarity index 97% rename from core/router/spa.go rename to core/router/router_spa_wrapper.go index c7fa2b54..6b6f0a13 100644 --- a/core/router/spa.go +++ b/core/router/router_spa_wrapper.go @@ -57,7 +57,7 @@ func (s *SPABuilder) isAsset(reqPath string) bool { // It should be passed to the router's `WrapRouter`: // https://godoc.org/github.com/kataras/iris/core/router#Router.WrapRouter // -// Example: https://github.com/kataras/iris/tree/master/_examples/beginner/file-server/single-page-application-builder +// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/single-page-application-builder func (s *SPABuilder) BuildWrapper(cPool *context.Pool) WrapperFunc { fileServer := s.AssetHandler diff --git a/core/router/status.go b/core/router/status.go index 1792b16b..8bfa4cee 100644 --- a/core/router/status.go +++ b/core/router/status.go @@ -1,7 +1,3 @@ -// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - package router import ( @@ -81,7 +77,7 @@ func defaultErrorCodeHandlers() *ErrorCodeHandlers { func statusText(statusCode int) context.Handler { return func(ctx context.Context) { if _, err := ctx.WriteString(http.StatusText(statusCode)); err != nil { - // ctx.Application().Log("(status code: %d) %s", + // ctx.Application().Logger().Infof("(status code: %d) %s", // err.Error(), statusCode) } } diff --git a/doc.go b/doc.go index d372f7d7..9ca4b75a 100644 --- a/doc.go +++ b/doc.go @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. +// Copyright (c) 2017 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 @@ -10,8 +10,8 @@ // 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 Gerasimos Maropoulos nor the name of his -// username, kataras, may be used to endorse or promote products derived from +// * 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 @@ -27,15 +27,19 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* -Package iris is a fully-featured HTTP/2 backend web framework written entirely in Google’s Go Language. +Package iris provides a beautifully expressive and easy to use foundation for your next website, API, or distributed app. Source code and other details for the project are available at GitHub: https://github.com/kataras/iris +Current Version + +8.0.0 + Installation -The only requirement is the Go Programming Language, at least version 1.8 +The only requirement is the Go Programming Language, at least version 1.8.x $ go get -u github.com/kataras/iris @@ -48,7 +52,6 @@ Example code: import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/view" ) // User is just a bindable object structure. @@ -66,7 +69,7 @@ Example code: // 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.AttachView(view.HTML("./views", ".html").Reload(true)) + app.RegisterView(iris.HTML("./views", ".html").Reload(true)) // Regster custom handler for specific http errors. app.OnErrorCode(iris.StatusInternalServerError, func(ctx context.Context) { @@ -81,7 +84,7 @@ Example code: }) app.Use(func(ctx context.Context) { - ctx.Application().Log("Begin request for path: %s", ctx.Path()) + ctx.Application().Logger().Infof("Begin request for path: %s", ctx.Path()) ctx.Next() }) @@ -123,7 +126,7 @@ Example code: } func logThisMiddleware(ctx context.Context) { - ctx.Application().Log("Path: %s | IP: %s", ctx.Path(), ctx.RemoteAddr()) + 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. @@ -161,6 +164,149 @@ Example code: ctx.View("users/create_verification.html") } +Listening and gracefully shutdown + +You can listen to a server using any type of net.Listener or http.Server instance. +The method for initialization of the server should be passed at the end, via `Run` function. + +Below you'll read some usage examples: + + + // Listening on tcp with network address 0.0.0.0:8080 + app.Run(iris.Addr(":8080")) + + + // Same as before but using a custom http.Server which may being used somewhere else too + app.Run(iris.Server(&http.Server{Addr:":8080"})) + + + // Using a custom net.Listener + l, err := net.Listen("tcp4", ":8080") + if err != nil { + panic(err) + } + app.Run(iris.Listener(l)) + + + // TLS using files + app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key")) + + + // Automatic TLS + app.Run(iris.AutoTLS("localhost:443")) + + + // UNIX socket + l, err := netutil.UNIX("/tmpl/srv.sock", 0666) + if err != nil { + panic(err) + } + + app.Run(iris.Listener(l)) + + // 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) + +UNIX and BSD hosts can take advandage of the reuse port feature. + +Example code: + + + package main + + import ( + // Package tcplisten provides customizable TCP net.Listener with various + // performance-related options: + // + // - SO_REUSEPORT. 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. + // + // - TCP_DEFER_ACCEPT. This option expects the server reads from the accepted + // connection before writing to them. + // + // - TCP_FASTOPEN. See https://lwn.net/Articles/508865/ for details. + "github.com/valyala/tcplisten" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + ) + + // $ go get github.com/valyala/tcplisten + // $ go run main.go + + func main() { + app := iris.New() + + app.Get("/", func(ctx context.Context) { + ctx.HTML("Hello World!") + }) + + listenerCfg := tcplisten.Config{ + ReusePort: true, + DeferAccept: true, + FastOpen: true, + } + + l, err := listenerCfg.NewListener("tcp", ":8080") + if err != nil { + panic(err) + } + + app.Run(iris.Listener(l)) + } + +That's all with listening, you have the full control when you need it. + +Let's continue by learning how to catch CONTROL+C/COMMAND+C or unix kill command and shutdown the server gracefuly. + + 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: + + + package main + + import ( + stdContext "context" + "time" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + ) + + + func main() { + app := iris.New() + + iris.RegisterOnInterrupt(func() { + timeout := 5 * time.Second + ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout) + defer cancel() + // close all hosts + app.Shutdown(ctx) + }) + + app.Get("/", func(ctx context.Context) { + ctx.HTML("

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

") + }) + + // http://localhost:8080 + app.Run(iris.Addr(":8080"), iris.WithoutInterruptHandler) + } + +Read more about listening and gracefully shutdown by navigating to: + + https://github.com/kataras/iris/tree/master/_examples/#http-listening + Routing @@ -180,7 +326,7 @@ Example code: }) -In order to make things easier for the user, Iris provides functions for all HTTP Methods. +In order to make things easier for the user, iris provides functions for all HTTP Methods. The first parameter is the request path of the route, second variadic parameter should contains one or more context.Handler executed by the registered order when a user requests for that specific resouce path from the server. @@ -252,7 +398,7 @@ Example code: Custom HTTP Errors -Iris developers are able to register their own handlers for http statuses like 404 not found, 500 internal server error and so on. +iris developers are able to register their own handlers for http statuses like 404 not found, 500 internal server error and so on. Example code: @@ -268,7 +414,7 @@ Example code: Basic HTTP API -With the help of Iris's expressionist router you can build any form of API you desire, with +With the help of iris's expressionist router you can build any form of API you desire, with safety. Example code: @@ -341,9 +487,9 @@ Example code: adminRoutes := app.Party("/admin", adminMiddleware) adminRoutes.Done(func(ctx context.Context) { // executes always last if ctx.Next() - ctx.Application().Log("response sent to " + ctx.Path()) + 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 intermediate/view examples. + // 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 context.Context) { @@ -430,9 +576,9 @@ Example code: func donateFinishHandler(ctx context.Context) { // values can be any type of object so we could cast the value to a string - // but Iris provides an easy to do that, if donate_url is not defined, then it returns an empty string instead. + // but iris provides an easy to do that, if donate_url is not defined, then it returns an empty string instead. donateURL := ctx.Values().GetString("donate_url") - ctx.Application().Log("donate_url value was: " + donateURL) + ctx.Application().Logger().Infof("donate_url value was: " + donateURL) ctx.Writef("\n\nDonate sent(?).") } @@ -447,15 +593,15 @@ At the previous example, 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 context.Context) +iris, like net/http std package registers route's handlers +by a Handler, the iris' type of handler is just a func(ctx context.Context) where context comes from github.com/kataras/iris/context. Until go 1.9 you will have to import that package too, after go 1.9 this will be not be necessary. -Iris has the easiest and the most powerful routing process you have ever meet. +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) +iris has its own interpeter(yes like a programming language) for route's path syntax and their dynamic path parameters parsing and evaluation, I am calling them "macros" for shortcut. How? It calculates its needs and if not any special regexp needed then it just @@ -510,7 +656,7 @@ i.e: {param:int min(3)} -Besides the fact that Iris provides the basic types and some default "macro funcs" +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: @@ -581,7 +727,7 @@ Example code: // let's use a trivial custom regexp that validates a single path parameter // which its value is only lowercase letters. - // http://localhost:8080/lowercase/kataras + // http://localhost:8080/lowercase/anylowercase app.Get("/lowercase/{name:string regexp(^[a-z]+)}", func(ctx context.Context) { ctx.Writef("name should be only lowercase, otherwise this handler will never executed: %s", ctx.Params().Get("name")) }) @@ -654,7 +800,7 @@ Static Files // // Returns the GET *Route. // - // Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/serve-embedded-files + // Example: https://github.com/kataras/iris/tree/master/_examples/file-server/embedding-files-into-app StaticEmbedded(requestPath string, vdir string, assetFn func(name string) ([]byte, error), namesFn func() []string) (*Route, error) // Favicon serves static favicon @@ -704,17 +850,17 @@ Example code: func main() { app := iris.New() - // This will serve the ./static/favicons/iris_32_32.ico to: localhost:8080/favicon.ico - app.Favicon("./static/favicons/iris_32_32.ico") + // This will serve the ./static/favicons/ion_32_32.ico to: localhost:8080/favicon.ico + app.Favicon("./static/favicons/ion_32_32.ico") - // app.Favicon("./static/favicons/iris_32_32.ico", "/favicon_48_48.ico") - // This will serve the ./static/favicons/iris_32_32.ico to: localhost:8080/favicon_48_48.ico + // app.Favicon("./static/favicons/ion_32_32.ico", "/favicon_48_48.ico") + // This will serve the ./static/favicons/ion_32_32.ico to: localhost:8080/favicon_48_48.ico app.Get("/", func(ctx context.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).`) + 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.Run(iris.Addr(":8080")) @@ -764,7 +910,7 @@ Example code: }) -Iris is able to wrap and convert any external, third-party Handler you used to use to your web application. +iris is able to wrap and convert any external, third-party Handler you used to use to your web application. Let's convert the https://github.com/rs/cors net/http external middleware which returns a `next form` handler. @@ -802,7 +948,7 @@ Example code: } func h(ctx context.Context) { - ctx.Application().Log(ctx.Path()) + ctx.Application().Logger().Infof(ctx.Path()) ctx.Writef("Hello from %s", ctx.Path()) } @@ -810,7 +956,7 @@ Example code: View Engine -Iris supports 5 template engines out-of-the-box, developers can still use any external golang template engine, +iris supports 5 template engines out-of-the-box, developers can still use any external golang template engine, as `context.ResponseWriter()` is an `io.Writer`. All of these five template engines have common features with common API, @@ -839,19 +985,18 @@ Example code: import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/view" ) func main() { app := iris.New() // defaults to these - // - standard html | view.HTML(...) - // - django | view.Django(...) - // - pug(jade) | view.Pug(...) - // - handlebars | view.Handlebars(...) - // - amber | view.Amber(...) + // - standard html | iris.HTML(...) + // - django | iris.Django(...) + // - pug(jade) | iris.Pug(...) + // - handlebars | iris.Handlebars(...) + // - amber | iris.Amber(...) - tmpl := view.HTML("./templates", ".html") + tmpl := iris.HTML("./templates", ".html") tmpl.Reload(true) // reload templates on each request (development mode) // default template funcs are: // @@ -863,7 +1008,7 @@ Example code: tmpl.AddFunc("greet", func(s string) string { return "Greetings " + s + "!" }) - app.AttachView(tmpl) + app.RegisterView(tmpl) app.Get("/", hi) @@ -873,7 +1018,7 @@ Example code: func hi(ctx context.Context) { ctx.ViewData("Title", "Hi Page") - ctx.ViewData("Name", "Iris") // {{.Name}} will render: Iris + ctx.ViewData("Name", "iris") // {{.Name}} will render: iris // ctx.ViewData("", myCcustomStruct{}) ctx.View("hi.html") } @@ -891,7 +1036,6 @@ Example code: import ( "github.com/kataras/iris" "github.com/kataras/iris/context" - "github.com/kataras/iris/view" ) func main() { @@ -901,7 +1045,7 @@ Example code: // $ go build // $ ./embedding-templates-into-app // html files are not used, you can delete the folder and run the example - app.AttachView(view.HTML("./templates", ".html").Binary(Asset, AssetNames)) + app.RegisterView(iris.HTML("./templates", ".html").Binary(Asset, AssetNames)) app.Get("/", hi) // http://localhost:8080 @@ -918,7 +1062,7 @@ Example code: } -A real example can be found here: https://github.com/kataras/iris/tree/master/_examples/intermediate/view/embedding-templates-into-app. +A real example can be found here: https://github.com/kataras/iris/tree/master/_examples/view/embedding-templates-into-app. Enable auto-reloading of templates on each request. Useful while developers are in dev mode as they no neeed to restart their app on every template edit. @@ -926,10 +1070,20 @@ as they no neeed to restart their app on every template edit. Example code: - pugEngine := view.Pug("./templates", ".jade") + pugEngine := iris.Pug("./templates", ".jade") pugEngine.Reload(true) // <--- set to true to re-build the templates on each request. - app.AttachView(pugEngine) + app.RegisterView(pugEngine) +Note: + +In case you're wondering, the code behind the view engines derives from the "github.com/kataras/iris/view" package, +access to the engines' variables can be granded by "github.com/kataras/iris" 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(...) Each one of these template engines has different options located here: https://github.com/kataras/iris/tree/master/view . @@ -950,33 +1104,35 @@ which logs him in. Additionally he can visit /logout to revoke his access to our Example code: - // sessions.go + // main.go package main import ( "github.com/kataras/iris" "github.com/kataras/iris/context" + "github.com/kataras/iris/sessions" ) var ( - key = "my_sessionid" + cookieNameForSessionID = "mycookiesessionnameid" + sess = sessions.New(sessions.Config{Cookie: cookieNameForSessionID}) ) func secret(ctx context.Context) { // Check if user is authenticated - if auth, _ := ctx.Session().GetBoolean("authenticated"); !auth { + if auth, _ := sess.Start(ctx).GetBoolean("authenticated"); !auth { ctx.StatusCode(iris.StatusForbidden) return } // Print secret message - ctx.Writef("The cake is a lie!") + ctx.WriteString("The cake is a lie!") } func login(ctx context.Context) { - session := ctx.Session() + session := sess.Start(ctx) // Authentication goes here // ... @@ -986,7 +1142,7 @@ Example code: } func logout(ctx context.Context) { - session := ctx.Session() + session := sess.Start(ctx) // Revoke users authentication session.Set("authenticated", false) @@ -995,9 +1151,6 @@ Example code: func main() { app := iris.New() - sess := sessions.New(sessions.Config{Cookie: key}) - app.AttachSessionManager(sess) - app.Get("/secret", secret) app.Get("/login", login) app.Get("/logout", logout) @@ -1005,19 +1158,135 @@ Example code: app.Run(iris.Addr(":8080")) } + Running the example: - $ go run sessions.go + + $ go get github.com/kataras/iris/sessions + $ go run main.go $ curl -s http://localhost:8080/secret Forbidden $ curl -s -I http://localhost:8080/login - Set-Cookie: mysessionid=MTQ4NzE5Mz... + Set-Cookie: mycookiesessionnameid=MTQ4NzE5Mz... - $ curl -s --cookie "mysessionid=MTQ4NzE5Mz..." http://localhost:8080/secret + $ curl -s --cookie "mycookiesessionnameid=MTQ4NzE5Mz..." http://localhost:8080/secret The cake is a lie! +More examples: + + https://github.com/kataras/iris/tree/master/sessions + + +Websockets + +In this example we will create a small chat between web sockets via browser. + +Example Server Code: + + // main.go + package main + + import ( + "fmt" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + + "github.com/kataras/iris/websocket" + ) + + func main() { + app := iris.New() + + app.Get("/", func(ctx context.Context) { + ctx.ServeFile("websockets.html", false) // second parameter: enable gzip? + }) + + setupWebsocket(app) + + // x2 + // http://localhost:8080 + // http://localhost:8080 + // write something, press submit, see the result. + app.Run(iris.Addr(":8080")) + } + + func setupWebsocket(app *iris.Application) { + // create our echo websocket server + ws := websocket.New(websocket.Config{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + }) + ws.OnConnection(handleConnection) + + // 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("/echo", ws.Handler()) + + // serve the javascript built'n client-side library, + // see weboskcets.html script tags, this path is used. + app.Any("/iris-ws.js", func(ctx context.Context) { + ctx.Write(websocket.ClientSource) + }) + } + + func handleConnection(c websocket.Connection) { + // Read events from browser + c.On("chat", func(msg string) { + // Print the message to the console, c.Context() is the iris's http context. + fmt.Printf("%s sent: %s\n", c.Context().RemoteAddr(), msg) + // Write message back to the client message owner: + // c.Emit("chat", msg) + c.To(websocket.Broadcast).Emit("chat", msg) + }) + } + +Example Client(javascript) Code: + + + + +

+    
+    
+
+
+Running the example:
+
+
+    $ go get github.com/kataras/iris/websocket
+    $ go run main.go
+    $ start http://localhost:8080
 
 
 That's the basics
@@ -1033,13 +1302,9 @@ Built'n Middleware:
 
     https://github.com/kataras/iris/tree/master/middleware
 
-Community Middleware:
-
-    https://github.com/iris-contrib/middleware
-
 Home Page:
 
-    http://iris-go.com
+    http://github.com/kataras/iris
 
 */
 package iris
diff --git a/httptest/LICENSE b/httptest/LICENSE
deleted file mode 100644
index fc8d2215..00000000
--- a/httptest/LICENSE
+++ /dev/null
@@ -1,52 +0,0 @@
-Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. 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 Gerasimos Maropoulos nor the name of his
-username, kataras, 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.
-
-
-Third-Parties:
-
-The MIT License (MIT)
-
-Copyright (c) 2016 Victor Gaydov
-
-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.
\ No newline at end of file
diff --git a/httptest/httptest.go b/httptest/httptest.go
index 16c1fded..ecad34fc 100644
--- a/httptest/httptest.go
+++ b/httptest/httptest.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package httptest
 
 import (
@@ -67,15 +63,16 @@ func DefaultConfiguration() *Configuration {
 }
 
 // New Prepares and returns a new test framework based on the "app".
-// You can find example on the https://github.com/kataras/iris/tree/master/_examples/intermediate/httptest
+// You can find example on the https://github.com/kataras/iris/tree/master/_examples/testing/httptest
 func New(t *testing.T, app *iris.Application, setters ...OptionSetter) *httpexpect.Expect {
 	conf := DefaultConfiguration()
 	for _, setter := range setters {
 		setter.Set(conf)
 	}
 
-	// disable logger
-	app.AttachLogger(nil)
+	// disable logger by setting it to the  "Panic" level, iris never uses this
+	// so it will never prints.
+	app.Logger().Level = 0
 	app.Build()
 
 	testConfiguration := httpexpect.Config{
diff --git a/httptest/status.go b/httptest/status.go
new file mode 100644
index 00000000..2c2c76d4
--- /dev/null
+++ b/httptest/status.go
@@ -0,0 +1,73 @@
+package httptest
+
+// HTTP status codes as registered with IANA.
+// See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+// Raw Copy from the net/http std package in order to recude the import path of "net/http" for the users.
+//
+// These may or may not stay.
+const (
+	StatusContinue           = 100 // RFC 7231, 6.2.1
+	StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2
+	StatusProcessing         = 102 // RFC 2518, 10.1
+
+	StatusOK                   = 200 // RFC 7231, 6.3.1
+	StatusCreated              = 201 // RFC 7231, 6.3.2
+	StatusAccepted             = 202 // RFC 7231, 6.3.3
+	StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4
+	StatusNoContent            = 204 // RFC 7231, 6.3.5
+	StatusResetContent         = 205 // RFC 7231, 6.3.6
+	StatusPartialContent       = 206 // RFC 7233, 4.1
+	StatusMultiStatus          = 207 // RFC 4918, 11.1
+	StatusAlreadyReported      = 208 // RFC 5842, 7.1
+	StatusIMUsed               = 226 // RFC 3229, 10.4.1
+
+	StatusMultipleChoices   = 300 // RFC 7231, 6.4.1
+	StatusMovedPermanently  = 301 // RFC 7231, 6.4.2
+	StatusFound             = 302 // RFC 7231, 6.4.3
+	StatusSeeOther          = 303 // RFC 7231, 6.4.4
+	StatusNotModified       = 304 // RFC 7232, 4.1
+	StatusUseProxy          = 305 // RFC 7231, 6.4.5
+	_                       = 306 // RFC 7231, 6.4.6 (Unused)
+	StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7
+	StatusPermanentRedirect = 308 // RFC 7538, 3
+
+	StatusBadRequest                   = 400 // RFC 7231, 6.5.1
+	StatusUnauthorized                 = 401 // RFC 7235, 3.1
+	StatusPaymentRequired              = 402 // RFC 7231, 6.5.2
+	StatusForbidden                    = 403 // RFC 7231, 6.5.3
+	StatusNotFound                     = 404 // RFC 7231, 6.5.4
+	StatusMethodNotAllowed             = 405 // RFC 7231, 6.5.5
+	StatusNotAcceptable                = 406 // RFC 7231, 6.5.6
+	StatusProxyAuthRequired            = 407 // RFC 7235, 3.2
+	StatusRequestTimeout               = 408 // RFC 7231, 6.5.7
+	StatusConflict                     = 409 // RFC 7231, 6.5.8
+	StatusGone                         = 410 // RFC 7231, 6.5.9
+	StatusLengthRequired               = 411 // RFC 7231, 6.5.10
+	StatusPreconditionFailed           = 412 // RFC 7232, 4.2
+	StatusRequestEntityTooLarge        = 413 // RFC 7231, 6.5.11
+	StatusRequestURITooLong            = 414 // RFC 7231, 6.5.12
+	StatusUnsupportedMediaType         = 415 // RFC 7231, 6.5.13
+	StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4
+	StatusExpectationFailed            = 417 // RFC 7231, 6.5.14
+	StatusTeapot                       = 418 // RFC 7168, 2.3.3
+	StatusUnprocessableEntity          = 422 // RFC 4918, 11.2
+	StatusLocked                       = 423 // RFC 4918, 11.3
+	StatusFailedDependency             = 424 // RFC 4918, 11.4
+	StatusUpgradeRequired              = 426 // RFC 7231, 6.5.15
+	StatusPreconditionRequired         = 428 // RFC 6585, 3
+	StatusTooManyRequests              = 429 // RFC 6585, 4
+	StatusRequestHeaderFieldsTooLarge  = 431 // RFC 6585, 5
+	StatusUnavailableForLegalReasons   = 451 // RFC 7725, 3
+
+	StatusInternalServerError           = 500 // RFC 7231, 6.6.1
+	StatusNotImplemented                = 501 // RFC 7231, 6.6.2
+	StatusBadGateway                    = 502 // RFC 7231, 6.6.3
+	StatusServiceUnavailable            = 503 // RFC 7231, 6.6.4
+	StatusGatewayTimeout                = 504 // RFC 7231, 6.6.5
+	StatusHTTPVersionNotSupported       = 505 // RFC 7231, 6.6.6
+	StatusVariantAlsoNegotiates         = 506 // RFC 2295, 8.1
+	StatusInsufficientStorage           = 507 // RFC 4918, 11.5
+	StatusLoopDetected                  = 508 // RFC 5842, 7.2
+	StatusNotExtended                   = 510 // RFC 2774, 7
+	StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
+)
diff --git a/iris.go b/iris.go
index edd4eebe..1eb298c3 100644
--- a/iris.go
+++ b/iris.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package iris
 
 import (
@@ -14,16 +10,20 @@ import (
 	"sync"
 	"time"
 
+	"github.com/sirupsen/logrus"
+
 	// context for the handlers
 	"github.com/kataras/iris/context"
 	// core packages, needed to build the application
 	"github.com/kataras/iris/core/errors"
 	"github.com/kataras/iris/core/host"
-	"github.com/kataras/iris/core/logger"
-	"github.com/kataras/iris/core/nettools"
+	"github.com/kataras/iris/core/netutil"
 	"github.com/kataras/iris/core/router"
-	// sessions and view
-	"github.com/kataras/iris/sessions"
+	// handlerconv conversions
+	"github.com/kataras/iris/core/handlerconv"
+	// cache conversions
+	"github.com/kataras/iris/cache"
+	// view
 	"github.com/kataras/iris/view"
 	// middleware used in Default method
 	requestLogger "github.com/kataras/iris/middleware/logger"
@@ -31,34 +31,103 @@ import (
 )
 
 const (
-	banner = `         _____      _
-        |_   _|    (_)
-          | |  ____ _  ___
-          | | | __|| |/ __|
-         _| |_| |  | |\__ \
-        |_____|_|  |_||___/ `
 
-	// Version is the current version number of the Iris Web framework.
-	//
-	// Look https://github.com/kataras/iris#where-can-i-find-older-versions for older versions.
-	Version = "7.2.0"
+	// Version is the current version number of the Iris Web Framework.
+	Version = "8.0.0"
 )
 
+// HTTP status codes as registered with IANA.
+// See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+// Raw Copy from the net/http std package in order to recude the import path of "net/http" for the users.
+//
+// Copied from `net/http` package.
 const (
-	// MethodNone is a Virtual method
-	// to store the "offline" routes.
-	//
-	// Conversion for router.MethodNone.
-	MethodNone = router.MethodNone
-	// NoLayout to disable layout for a particular template file
-	// Conversion for view.NoLayout.
-	NoLayout = view.NoLayout
+	StatusContinue           = 100 // RFC 7231, 6.2.1
+	StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2
+	StatusProcessing         = 102 // RFC 2518, 10.1
+
+	StatusOK                   = 200 // RFC 7231, 6.3.1
+	StatusCreated              = 201 // RFC 7231, 6.3.2
+	StatusAccepted             = 202 // RFC 7231, 6.3.3
+	StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4
+	StatusNoContent            = 204 // RFC 7231, 6.3.5
+	StatusResetContent         = 205 // RFC 7231, 6.3.6
+	StatusPartialContent       = 206 // RFC 7233, 4.1
+	StatusMultiStatus          = 207 // RFC 4918, 11.1
+	StatusAlreadyReported      = 208 // RFC 5842, 7.1
+	StatusIMUsed               = 226 // RFC 3229, 10.4.1
+
+	StatusMultipleChoices   = 300 // RFC 7231, 6.4.1
+	StatusMovedPermanently  = 301 // RFC 7231, 6.4.2
+	StatusFound             = 302 // RFC 7231, 6.4.3
+	StatusSeeOther          = 303 // RFC 7231, 6.4.4
+	StatusNotModified       = 304 // RFC 7232, 4.1
+	StatusUseProxy          = 305 // RFC 7231, 6.4.5
+	_                       = 306 // RFC 7231, 6.4.6 (Unused)
+	StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7
+	StatusPermanentRedirect = 308 // RFC 7538, 3
+
+	StatusBadRequest                   = 400 // RFC 7231, 6.5.1
+	StatusUnauthorized                 = 401 // RFC 7235, 3.1
+	StatusPaymentRequired              = 402 // RFC 7231, 6.5.2
+	StatusForbidden                    = 403 // RFC 7231, 6.5.3
+	StatusNotFound                     = 404 // RFC 7231, 6.5.4
+	StatusMethodNotAllowed             = 405 // RFC 7231, 6.5.5
+	StatusNotAcceptable                = 406 // RFC 7231, 6.5.6
+	StatusProxyAuthRequired            = 407 // RFC 7235, 3.2
+	StatusRequestTimeout               = 408 // RFC 7231, 6.5.7
+	StatusConflict                     = 409 // RFC 7231, 6.5.8
+	StatusGone                         = 410 // RFC 7231, 6.5.9
+	StatusLengthRequired               = 411 // RFC 7231, 6.5.10
+	StatusPreconditionFailed           = 412 // RFC 7232, 4.2
+	StatusRequestEntityTooLarge        = 413 // RFC 7231, 6.5.11
+	StatusRequestURITooLong            = 414 // RFC 7231, 6.5.12
+	StatusUnsupportedMediaType         = 415 // RFC 7231, 6.5.13
+	StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4
+	StatusExpectationFailed            = 417 // RFC 7231, 6.5.14
+	StatusTeapot                       = 418 // RFC 7168, 2.3.3
+	StatusUnprocessableEntity          = 422 // RFC 4918, 11.2
+	StatusLocked                       = 423 // RFC 4918, 11.3
+	StatusFailedDependency             = 424 // RFC 4918, 11.4
+	StatusUpgradeRequired              = 426 // RFC 7231, 6.5.15
+	StatusPreconditionRequired         = 428 // RFC 6585, 3
+	StatusTooManyRequests              = 429 // RFC 6585, 4
+	StatusRequestHeaderFieldsTooLarge  = 431 // RFC 6585, 5
+	StatusUnavailableForLegalReasons   = 451 // RFC 7725, 3
+
+	StatusInternalServerError           = 500 // RFC 7231, 6.6.1
+	StatusNotImplemented                = 501 // RFC 7231, 6.6.2
+	StatusBadGateway                    = 502 // RFC 7231, 6.6.3
+	StatusServiceUnavailable            = 503 // RFC 7231, 6.6.4
+	StatusGatewayTimeout                = 504 // RFC 7231, 6.6.5
+	StatusHTTPVersionNotSupported       = 505 // RFC 7231, 6.6.6
+	StatusVariantAlsoNegotiates         = 506 // RFC 2295, 8.1
+	StatusInsufficientStorage           = 507 // RFC 4918, 11.5
+	StatusLoopDetected                  = 508 // RFC 5842, 7.2
+	StatusNotExtended                   = 510 // RFC 2774, 7
+	StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
 )
 
+// HTTP Methods copied from `net/http`.
+const (
+	MethodGet     = "GET"
+	MethodPost    = "POST"
+	MethodPut     = "PUT"
+	MethodDelete  = "DELETE"
+	MethodConnect = "CONNECT"
+	MethodHead    = "HEAD"
+	MethodPatch   = "PATCH"
+	MethodOptions = "OPTIONS"
+	MethodTrace   = "TRACE"
+)
+
+// MethodNone is an iris-specific "virtual" method
+// to store the "offline" routes.
+const MethodNone = "NONE"
+
 // Application is responsible to manage the state of the application.
 // It contains and handles all the necessary parts to create a fast web server.
 type Application struct {
-	Scheduler host.Scheduler
 	// routing embedded | exposing APIBuilder's and Router's public API.
 	*router.APIBuilder
 	*router.Router
@@ -68,30 +137,33 @@ type Application struct {
 	// all fields defaults to something that is working, developers don't have to set it.
 	config *Configuration
 
-	// logger logs to the defined logger.
-	// Use AttachLogger to change the default which prints messages to the os.Stdout.
-	// It's just an io.Writer, period.
-	logger io.Writer
+	// the logrus logger instance, defaults to "Info" level messages (all except "Debug")
+	logger *logrus.Logger
 
 	// view engine
 	view view.View
-
-	// sessions and flash messages
-	sessions sessions.Sessions
 	// used for build
 	once sync.Once
 
-	mu       sync.Mutex
-	Shutdown func(stdContext.Context) error
+	mu sync.Mutex
+	// Hosts contains a list of all servers (Host Supervisors) that this app is running on.
+	//
+	// Hosts may be empty only if application ran(`app.Run`) with `iris.Raw` option runner,
+	// otherwise it contains a single host (`app.Hosts[0]`).
+	//
+	// Additional Host Supervisors can be added to that list by calling the `app.NewHost` manually.
+	//
+	// Hosts field is available after `Run` or `NewHost`.
+	Hosts []*host.Supervisor
 }
 
-// New creates and returns a fresh empty Iris *Application instance.
+// New creates and returns a fresh empty iris *Application instance.
 func New() *Application {
 	config := DefaultConfiguration()
 
 	app := &Application{
 		config:     &config,
-		logger:     logger.NewDevLogger(banner),
+		logger:     logrus.New(),
 		APIBuilder: router.NewAPIBuilder(),
 		Router:     router.NewRouter(),
 	}
@@ -103,20 +175,10 @@ func New() *Application {
 	return app
 }
 
-// Default returns a new Application instance.
-// Unlike `New` this method prepares some things for you.
-// std html templates from the "./templates" directory,
-// session manager is attached with a default expiration of 7 days,
-// recovery and (request) logger handlers(middleware) are being registered.
+// Default returns a new Application instance which, unlike `New`,
+// recovers on panics and logs the incoming http requests.
 func Default() *Application {
 	app := New()
-
-	app.AttachView(view.HTML("./templates", ".html"))
-	app.AttachSessionManager(sessions.New(sessions.Config{
-		Cookie:  "irissessionid",
-		Expires: 7 * (24 * time.Hour), // 1 week
-	}))
-
 	app.Use(recover.New())
 	app.Use(requestLogger.New())
 
@@ -128,7 +190,7 @@ func Default() *Application {
 // and returns an error which if it's not nil it's printed to the logger.
 // See configuration.go for more.
 //
-// Returns itself in order to be used like app:= New().Configure(...)
+// Returns itself in order to be used like `app:= New().Configure(...)`
 func (app *Application) Configure(configurators ...Configurator) *Application {
 	for _, cfg := range configurators {
 		cfg(app)
@@ -137,36 +199,119 @@ func (app *Application) Configure(configurators ...Configurator) *Application {
 	return app
 }
 
-// Build sets up, once, the framework.
-// It builds the default router with its default macros
-// and the template functions that are very-closed to Iris.
-func (app *Application) Build() (err error) {
-	app.once.Do(func() {
-		// view engine
-		// here is where we declare the closed-relative framework functions.
-		// Each engine has their defaults, i.e yield,render,render_r,partial, params...
-		rv := router.NewRoutePathReverser(app.APIBuilder)
-		app.view.AddFunc("urlpath", rv.Path)
-		// app.view.AddFunc("url", rv.URL)
-		err = app.view.Load()
-		if err != nil {
-			return // if view engine loading failed then don't continue
-		}
+// ConfigurationReadOnly returns an object which doesn't allow field writing.
+func (app *Application) ConfigurationReadOnly() context.ConfigurationReadOnly {
+	return app.config
+}
 
-		if !app.Router.Downgraded() {
-			var routerHandler router.RequestHandler
-			// router
-			// create the request handler, the default routing handler
-			routerHandler = router.NewDefaultHandler()
+// These are the different logging levels. You can set the logging level to log
+// on the application 's instance of logger, obtained with `app.Logger()`.
+//
+// These are conversions from logrus.
+const (
+	// NoLog level, logs nothing.
+	// It's the logrus' `PanicLevel` but it never used inside iris so it will never log.
+	NoLog = logrus.PanicLevel
+	// ErrorLevel level. Logs. Used for errors that should definitely be noted.
+	// Commonly used for hooks to send errors to an error tracking service.
+	ErrorLevel = logrus.ErrorLevel
+	// WarnLevel level. Non-critical entries that deserve eyes.
+	WarnLevel = logrus.WarnLevel
+)
 
-			err = app.Router.BuildRouter(app.ContextPool, routerHandler, app.APIBuilder)
-			// re-build of the router from outside can be done with;
-			// app.RefreshRouter()
-		}
+// Logger returns the logrus logger instance(pointer) that is being used inside the "app".
+func (app *Application) Logger() *logrus.Logger {
+	return app.logger
+}
 
-	})
+var (
+	// HTML view engine.
+	// Conversion for the view.HTML.
+	HTML = view.HTML
+	// Django view engine.
+	// Conversion for the view.Django.
+	Django = view.Django
+	// Handlebars view engine.
+	// Conversion for the view.Handlebars.
+	Handlebars = view.Handlebars
+	// Pug view engine.
+	// Conversion for the view.Pug.
+	Pug = view.Pug
+	// Amber view engine.
+	// Conversion for the view.Amber.
+	Amber = view.Amber
+)
 
-	return
+// NoLayout to disable layout for a particular template file
+// A shortcut for the `view#NoLayout`.
+const NoLayout = view.NoLayout
+
+// RegisterView should be used to register view engines mapping to a root directory
+// and the template file(s) extension.
+func (app *Application) RegisterView(viewEngine view.Engine) {
+	app.view.Register(viewEngine)
+}
+
+// View executes and writes the result of a template file to the writer.
+//
+// First parameter is the writer to write the parsed template.
+// Second parameter is the relative, to templates directory, template filename, including extension.
+// Third parameter is the layout, can be empty string.
+// Forth parameter is the bindable data to the template, can be nil.
+//
+// Use context.View to render templates to the client instead.
+// Returns an error on failure, otherwise nil.
+func (app *Application) View(writer io.Writer, filename string, layout string, bindingData interface{}) error {
+	if app.view.Len() == 0 {
+		err := errors.New("view engine is missing, use `RegisterView`")
+		app.Logger().Errorln(err)
+		return err
+	}
+
+	err := app.view.ExecuteWriter(writer, filename, layout, bindingData)
+	if err != nil {
+		app.Logger().Errorln(err)
+	}
+	return err
+}
+
+var (
+	// LimitRequestBodySize is a middleware which sets a request body size limit
+	// for all next handlers in the chain.
+	//
+	// A shortcut for the `context#LimitRequestBodySize`.
+	LimitRequestBodySize = context.LimitRequestBodySize
+	// FromStd converts native http.Handler, http.HandlerFunc & func(w, r, next) to context.Handler.
+	//
+	// Supported form types:
+	// 		 .FromStd(h http.Handler)
+	// 		 .FromStd(func(w http.ResponseWriter, r *http.Request))
+	// 		 .FromStd(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc))
+	//
+	// A shortcut for the `handlerconv#FromStd`.
+	FromStd = handlerconv.FromStd
+	// Cache is a middleware providing cache functionalities
+	// to the next handlers, can be used as: `app.Get("/", iris.Cache, aboutHandler)`.
+	//
+	// Examples can be found at: https://github.com/kataras/iris/tree/master/_examples/#caching
+	Cache = cache.Handler
+)
+
+// SPA  accepts an "assetHandler" which can be the result of an
+// app.StaticHandler or app.StaticEmbeddedHandler.
+// It wraps the router and checks:
+// if it;s an asset, if the request contains "." (this behavior can be changed via /core/router.NewSPABuilder),
+// if the request is index, redirects back to the "/" in order to let the root handler to be executed,
+// if it's not an asset then it executes the router, so the rest of registered routes can be
+// executed without conflicts with the file server ("assetHandler").
+//
+// Use that instead of `StaticWeb` for root "/" file server.
+//
+// Example: https://github.com/kataras/iris/tree/master/_examples/file-server/single-page-application
+func (app *Application) SPA(assetHandler context.Handler) {
+	s := router.NewSPABuilder(assetHandler)
+	wrapper := s.BuildWrapper(app.ContextPool)
+	app.Router.WrapRouter(wrapper)
 }
 
 // NewHost accepts a standar *http.Server object,
@@ -183,7 +328,7 @@ func (app *Application) NewHost(srv *http.Server) *host.Supervisor {
 
 	// check if different ErrorLog provided, if not bind it with the framework's logger
 	if srv.ErrorLog == nil {
-		srv.ErrorLog = log.New(app.logger, "[HTTP Server] ", 0)
+		srv.ErrorLog = log.New(app.logger.Out, "[HTTP Server] ", 0)
 	}
 
 	if srv.Addr == "" {
@@ -201,38 +346,42 @@ func (app *Application) NewHost(srv *http.Server) *host.Supervisor {
 		// sub.mydomain.com -> valid
 		// sub.localhost -> valid
 		// we need the host (without port if 80 or 443) in order to validate these, so:
-		app.config.vhost = nettools.ResolveVHost(srv.Addr)
+		app.config.vhost = netutil.ResolveVHost(srv.Addr)
 	}
 	// the below schedules some tasks that will run among the server
 
-	// I was thinking to have them on Default or here and if user not wanted these, could use a custom core/host
-	// but that's too much for someone to just disable the banner for example,
-	// so I will bind them to a configuration field, although is not direct to the *Application,
-	// host is de-coupled from *Application as the other features too, it took me 2 months for this design.
-
-	// copy the registered schedule tasks from the scheduler, if any will be copied to this host supervisor's scheduler.
-	app.Scheduler.CopyTo(&su.Scheduler)
-
-	if !app.config.DisableBanner {
-		// show the banner and the available keys to exit from app.
-		su.Schedule(host.WriteBannerTask(app.logger, banner+"V"+Version))
+	if !app.config.DisableStartupLog {
+		// show the available info to exit from app.
+		su.RegisterOnServe(host.WriteStartupLogOnServe(app.logger.Out)) // app.logger.Writer -> Info
 	}
 
 	if !app.config.DisableInterruptHandler {
-		// give 5 seconds to the server to wait for the (idle) connections.
-		shutdownTimeout := 5 * time.Second
-
 		// when CTRL+C/CMD+C pressed.
-		su.Schedule(host.ShutdownOnInterruptTask(shutdownTimeout))
+		shutdownTimeout := 5 * time.Second
+		host.RegisterOnInterrupt(host.ShutdownOnInterrupt(su, shutdownTimeout))
 	}
 
-	if app.Shutdown == nil {
-		app.Shutdown = su.Shutdown
-	}
+	app.Hosts = append(app.Hosts, su)
 
 	return su
 }
 
+// RegisterOnInterrupt registers a global function to call when CTRL+C/CMD+C pressed or a unix kill command received.
+//
+// A shortcut for the `host#RegisterOnInterrupt`.
+var RegisterOnInterrupt = host.RegisterOnInterrupt
+
+// Shutdown gracefully terminates all the application's server hosts.
+// Returns an error on the first failure, otherwise nil.
+func (app *Application) Shutdown(ctx stdContext.Context) error {
+	for _, su := range app.Hosts {
+		if err := su.Shutdown(ctx); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
 // Runner is just an interface which accepts the framework instance
 // and returns an error.
 //
@@ -250,7 +399,7 @@ type Runner func(*Application) error
 // See `Run` for more.
 func Listener(l net.Listener) Runner {
 	return func(app *Application) error {
-		app.config.vhost = nettools.ResolveVHost(l.Addr().String())
+		app.config.vhost = netutil.ResolveVHost(l.Addr().String())
 		return app.NewHost(new(http.Server)).
 			Serve(l)
 	}
@@ -318,7 +467,7 @@ func AutoTLS(addr string) Runner {
 // only when the server exited or a fatal error caused.
 //
 // With this option you're not limited to the servers
-// that Iris can run by-default.
+// that iris can run by-default.
 //
 // See `Run` for more.
 func Raw(f func() error) Runner {
@@ -327,6 +476,45 @@ func Raw(f func() error) Runner {
 	}
 }
 
+// Build sets up, once, the framework.
+// It builds the default router with its default macros
+// and the template functions that are very-closed to iris.
+func (app *Application) Build() error {
+	rp := errors.NewReporter()
+
+	app.once.Do(func() {
+		rp.Describe("api builder: %v", app.APIBuilder.GetReport())
+
+		if !app.Router.Downgraded() {
+			// router
+			// create the request handler, the default routing handler
+			routerHandler := router.NewDefaultHandler()
+
+			rp.Describe("router: %v", app.Router.BuildRouter(app.ContextPool, routerHandler, app.APIBuilder))
+			// re-build of the router from outside can be done with;
+			// app.RefreshRouter()
+		}
+
+		if app.view.Len() > 0 {
+			// view engine
+			// here is where we declare the closed-relative framework functions.
+			// Each engine has their defaults, i.e yield,render,render_r,partial, params...
+			rv := router.NewRoutePathReverser(app.APIBuilder)
+			app.view.AddFunc("urlpath", rv.Path)
+			// app.view.AddFunc("url", rv.URL)
+			rp.Describe("view: %v", app.view.Load())
+		}
+	})
+
+	return rp.Return()
+}
+
+// ErrServerClosed is returned by the Server's Serve, ServeTLS, ListenAndServe,
+// and ListenAndServeTLS methods after a call to Shutdown or Close.
+//
+// Conversion for the http.ErrServerClosed.
+var ErrServerClosed = http.ErrServerClosed
+
 // Run builds the framework and starts the desired `Runner` with or without configuration edits.
 //
 // Run should be called only once per Application instance, it blocks like http.Server.
@@ -335,7 +523,7 @@ func Raw(f func() error) Runner {
 // then create a new host and run it manually by `go NewHost(*http.Server).Serve/ListenAndServe` etc...
 // or use an already created host:
 // h := NewHost(*http.Server)
-// Run(Raw(h.ListenAndServe), WithoutBanner, WithCharset("UTF-8"))
+// Run(Raw(h.ListenAndServe), WithCharset("UTF-8"), WithRemoteAddrHeader("CF-Connecting-IP"))
 //
 // The Application can go online with any type of server or iris's host with the help of
 // the following runners:
@@ -344,91 +532,15 @@ func (app *Application) Run(serve Runner, withOrWithout ...Configurator) error {
 	// first Build because it doesn't need anything from configuration,
 	//  this give the user the chance to modify the router inside a configurator as well.
 	if err := app.Build(); err != nil {
-		return err
+		return errors.PrintAndReturnErrors(err, app.logger.Errorf)
 	}
 
 	app.Configure(withOrWithout...)
 
 	// this will block until an error(unless supervisor's DeferFlow called from a Task).
-	return serve(app)
-}
-
-// AttachLogger attachs a new logger to the framework.
-func (app *Application) AttachLogger(logWriter io.Writer) {
-	if logWriter == nil {
-		// convert that to an empty writerFunc
-		logWriter = logger.NoOpLogger
+	err := serve(app)
+	if err != nil {
+		app.Logger().Errorln(err)
 	}
-	app.logger = logWriter
-}
-
-// Log sends a message to the defined io.Writer logger, it's
-// just a help function for internal use but it can be used to a cusotom middleware too.
-//
-// See AttachLogger too.
-func (app *Application) Log(format string, a ...interface{}) {
-	logger.Log(app.logger, format, a...)
-}
-
-// AttachView should be used to register view engines mapping to a root directory
-// and the template file(s) extension.
-// Returns an error on failure, otherwise nil.
-func (app *Application) AttachView(viewEngine view.Engine) error {
-	return app.view.Register(viewEngine)
-}
-
-// View executes and writes the result of a template file to the writer.
-//
-// First parameter is the writer to write the parsed template.
-// Second parameter is the relative, to templates directory, template filename, including extension.
-// Third parameter is the layout, can be empty string.
-// Forth parameter is the bindable data to the template, can be nil.
-//
-// Use context.View to render templates to the client instead.
-// Returns an error on failure, otherwise nil.
-func (app *Application) View(writer io.Writer, filename string, layout string, bindingData interface{}) error {
-	if app.view.Len() == 0 {
-		return errors.New("view engine is missing")
-	}
-	return app.view.ExecuteWriter(writer, filename, layout, bindingData)
-}
-
-// AttachSessionManager registers a session manager to the framework which is used for flash messages too.
-//
-// See context.Session too.
-func (app *Application) AttachSessionManager(manager sessions.Sessions) {
-	app.sessions = manager
-}
-
-// SessionManager returns the session manager which contain a Start and Destroy methods
-// used inside the context.Session().
-//
-// It's ready to use after the RegisterSessions.
-func (app *Application) SessionManager() (sessions.Sessions, error) {
-	if app.sessions == nil {
-		return nil, errors.New("session manager is missing")
-	}
-	return app.sessions, nil
-}
-
-// SPA  accepts an "assetHandler" which can be the result of an
-// app.StaticHandler or app.StaticEmbeddedHandler.
-// It wraps the router and checks:
-// if it;s an asset, if the request contains "." (this behavior can be changed via /core/router.NewSPABuilder),
-// if the request is index, redirects back to the "/" in order to let the root handler to be executed,
-// if it's not an asset then it executes the router, so the rest of registered routes can be
-// executed without conflicts with the file server ("assetHandler").
-//
-// Use that instead of `StaticWeb` for root "/" file server.
-//
-// Example: https://github.com/kataras/iris/tree/master/_examples/beginner/file-server/single-page-application
-func (app *Application) SPA(assetHandler context.Handler) {
-	s := router.NewSPABuilder(assetHandler)
-	wrapper := s.BuildWrapper(app.ContextPool)
-	app.Router.WrapRouter(wrapper)
-}
-
-// ConfigurationReadOnly returns a structure which doesn't allow writing.
-func (app *Application) ConfigurationReadOnly() context.ConfigurationReadOnly {
-	return app.config
+	return err
 }
diff --git a/iris_deprecated.go b/iris_deprecated.go
deleted file mode 100644
index 40baf21b..00000000
--- a/iris_deprecated.go
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package iris
-
-// The below code will be removed at the next release.
-// It's here to make your overall experience more familiar with the APIs you used before Iris.
-import (
-	"net"
-	"net/url"
-	"os"
-
-	"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/nettools"
-)
-
-// ToHandler converts native http.Handler & http.HandlerFunc to context.Handler.
-//
-// Supported form types:
-// 		 .ToHandler(h http.Handler)
-// 		 .ToHandler(func(w http.ResponseWriter, r *http.Request))
-// 		 .ToHandler(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc))
-//
-// Deprecated. Use the "core/handlerconv" package instead, equivalent to `ToHandler` is its `FromStd` function.
-func ToHandler(handler interface{}) context.Handler {
-	return handlerconv.FromStd(handler)
-}
-
-// Cache provides cache capabilities to a route's handler.
-// Usage:
-// Get("/", iris.Cache(time.Duration(10*time.Second)), func(ctx context.Context){
-//    ctx.Writef("Hello, world!") // or a template or anything else
-// })
-//
-// Deprecated. Use "github.com/kataras/iris/cache" sub-package which contains the full features instead.
-var Cache = cache.Handler
-
-// CheckErr is the old `Must`. It panics on errors as expected with
-// the old listen functions, change of this method will affect only ListenXXX functions.
-//
-// Its only callers are the following, deprecated, listen functions.
-var CheckErr = func(err error) {
-	if err != nil {
-		panic(err)
-	}
-}
-
-// Serve serves incoming connections from the given listener.
-//
-// Serve blocks until the given listener returns permanent error.
-//
-// Deprecated. Use `Run` instead.
-func (app *Application) Serve(l net.Listener) error {
-	return app.Run(Listener(l))
-}
-
-// Listen starts the standalone http server
-// which listens to the addr parameter which as the form of
-// host:port
-//
-// Deprecated. Use `Run` instead.
-func (app *Application) Listen(addr string) {
-	CheckErr(app.Run(Addr(addr)))
-}
-
-// ListenUNIX starts the server listening to the new requests using a 'socket file'.
-// Note: this works only on unix.
-//
-// Deprecated. Use `Run` instead.
-func (app *Application) ListenUNIX(socketFile string, mode os.FileMode) {
-	l, err := nettools.UNIX(socketFile, mode)
-	CheckErr(err)
-
-	CheckErr(app.Run(Listener(l)))
-}
-
-// ListenTLS starts the secure server with provided certificates,
-// if you use this method the requests of the form of 'http://' will fail
-// only https:// connections are allowed
-// which listens to the addr parameter which as the form of
-// host:port
-//
-//
-// Deprecated. Use `Run` instead.
-func (app *Application) ListenTLS(addr string, certFile, keyFile string) {
-	CheckErr(app.Run(TLS(addr, certFile, keyFile)))
-}
-
-// ListenLETSENCRYPT starts the server listening at the specific nat address
-// using key & certification taken from the letsencrypt.org 's servers
-// it's also starts a second 'http' server to redirect all 'http://$ADDR/$PATH' to the' https://$ADDR/$PATH'
-// it creates a cache file to store the certifications, for performance reasons, this file by-default is "./certcache"
-// if you skip the second parameter then the cache file is "./letsencrypt.cache"
-// if you want to disable cache then simple pass as second argument an empty empty string ""
-//
-// Deprecated. Use `Run` instead.
-func (app *Application) ListenLETSENCRYPT(addr string, cacheDirOptional ...string) {
-	l, err := nettools.LETSENCRYPT(addr, addr, cacheDirOptional...)
-	CheckErr(err)
-	// create the redirect server to redirect http://... to https://...
-	hostname := nettools.ResolveHostname(addr)
-	proxyAddr := hostname + ":80"
-	target, err := url.Parse("https://" + hostname)
-	CheckErr(err)
-	go host.NewProxy(proxyAddr, target).ListenAndServe()
-
-	CheckErr(app.Run(Listener(l)))
-}
-
-// OnStatusCode registers an error http status code
-// based on the "statusCode" >= 400.
-// The handler is being wrapepd by a generic
-// handler which will try to reset
-// the body if recorder was enabled
-// and/or disable the gzip if gzip response recorder
-// was active.
-//
-// Deprecated. Use `OnErrorCode` instead.
-func (app *Application) OnStatusCode(statusCode int, handler context.Handler) {
-	app.OnErrorCode(statusCode, handler)
-}
-
-// HTTP status codes as registered with IANA.
-// See: http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
-// Raw Copy from the net/http std package in order to recude the import path of "net/http" for the users.
-//
-// These may or may not stay.
-const (
-	StatusContinue           = 100 // RFC 7231, 6.2.1
-	StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2
-	StatusProcessing         = 102 // RFC 2518, 10.1
-
-	StatusOK                   = 200 // RFC 7231, 6.3.1
-	StatusCreated              = 201 // RFC 7231, 6.3.2
-	StatusAccepted             = 202 // RFC 7231, 6.3.3
-	StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4
-	StatusNoContent            = 204 // RFC 7231, 6.3.5
-	StatusResetContent         = 205 // RFC 7231, 6.3.6
-	StatusPartialContent       = 206 // RFC 7233, 4.1
-	StatusMultiStatus          = 207 // RFC 4918, 11.1
-	StatusAlreadyReported      = 208 // RFC 5842, 7.1
-	StatusIMUsed               = 226 // RFC 3229, 10.4.1
-
-	StatusMultipleChoices   = 300 // RFC 7231, 6.4.1
-	StatusMovedPermanently  = 301 // RFC 7231, 6.4.2
-	StatusFound             = 302 // RFC 7231, 6.4.3
-	StatusSeeOther          = 303 // RFC 7231, 6.4.4
-	StatusNotModified       = 304 // RFC 7232, 4.1
-	StatusUseProxy          = 305 // RFC 7231, 6.4.5
-	_                       = 306 // RFC 7231, 6.4.6 (Unused)
-	StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7
-	StatusPermanentRedirect = 308 // RFC 7538, 3
-
-	StatusBadRequest                   = 400 // RFC 7231, 6.5.1
-	StatusUnauthorized                 = 401 // RFC 7235, 3.1
-	StatusPaymentRequired              = 402 // RFC 7231, 6.5.2
-	StatusForbidden                    = 403 // RFC 7231, 6.5.3
-	StatusNotFound                     = 404 // RFC 7231, 6.5.4
-	StatusMethodNotAllowed             = 405 // RFC 7231, 6.5.5
-	StatusNotAcceptable                = 406 // RFC 7231, 6.5.6
-	StatusProxyAuthRequired            = 407 // RFC 7235, 3.2
-	StatusRequestTimeout               = 408 // RFC 7231, 6.5.7
-	StatusConflict                     = 409 // RFC 7231, 6.5.8
-	StatusGone                         = 410 // RFC 7231, 6.5.9
-	StatusLengthRequired               = 411 // RFC 7231, 6.5.10
-	StatusPreconditionFailed           = 412 // RFC 7232, 4.2
-	StatusRequestEntityTooLarge        = 413 // RFC 7231, 6.5.11
-	StatusRequestURITooLong            = 414 // RFC 7231, 6.5.12
-	StatusUnsupportedMediaType         = 415 // RFC 7231, 6.5.13
-	StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4
-	StatusExpectationFailed            = 417 // RFC 7231, 6.5.14
-	StatusTeapot                       = 418 // RFC 7168, 2.3.3
-	StatusUnprocessableEntity          = 422 // RFC 4918, 11.2
-	StatusLocked                       = 423 // RFC 4918, 11.3
-	StatusFailedDependency             = 424 // RFC 4918, 11.4
-	StatusUpgradeRequired              = 426 // RFC 7231, 6.5.15
-	StatusPreconditionRequired         = 428 // RFC 6585, 3
-	StatusTooManyRequests              = 429 // RFC 6585, 4
-	StatusRequestHeaderFieldsTooLarge  = 431 // RFC 6585, 5
-	StatusUnavailableForLegalReasons   = 451 // RFC 7725, 3
-
-	StatusInternalServerError           = 500 // RFC 7231, 6.6.1
-	StatusNotImplemented                = 501 // RFC 7231, 6.6.2
-	StatusBadGateway                    = 502 // RFC 7231, 6.6.3
-	StatusServiceUnavailable            = 503 // RFC 7231, 6.6.4
-	StatusGatewayTimeout                = 504 // RFC 7231, 6.6.5
-	StatusHTTPVersionNotSupported       = 505 // RFC 7231, 6.6.6
-	StatusVariantAlsoNegotiates         = 506 // RFC 2295, 8.1
-	StatusInsufficientStorage           = 507 // RFC 4918, 11.5
-	StatusLoopDetected                  = 508 // RFC 5842, 7.2
-	StatusNotExtended                   = 510 // RFC 2774, 7
-	StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
-)
-
-// These may or may not stay, you can use net/http's constants too.
-const (
-	MethodGet     = "GET"
-	MethodPost    = "POST"
-	MethodPut     = "PUT"
-	MethodDelete  = "DELETE"
-	MethodConnect = "CONNECT"
-	MethodHead    = "HEAD"
-	MethodPatch   = "PATCH"
-	MethodOptions = "OPTIONS"
-	MethodTrace   = "TRACE"
-	// MethodNone is declared at iris.go, it will stay.
-)
diff --git a/middleware/README.md b/middleware/README.md
new file mode 100644
index 00000000..7fe1cb44
--- /dev/null
+++ b/middleware/README.md
@@ -0,0 +1,49 @@
+Built'n Handlers
+------------
+
+| Middleware | Example |
+| -----------|-------------|
+| [basic authentication](basicauth) | [iris/_examples/authentication/basicauth](https://github.com/kataras/iris/tree/master/_examples/authentication/basicauth) |
+| [localization and internationalization](i18n) | [iris/_examples/miscellaneous/i81n](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/i18n) |
+| [request logger](logger) | [iris/_examples/http_request/request-logger](https://github.com/kataras/iris/tree/master/_examples/http_request/request-logger) |
+| [profiling (pprof)](pprof) | [iris/_examples/miscellaneous/pprof](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/pprof) |
+| [recovery](recover) | [iris/_examples/miscellaneous/recover](https://github.com/kataras/iris/tree/master/_examples/miscellaneous/recover) |
+
+Experimental Handlers
+------------
+
+Most of the experimental handlers are ported to work with _iris_'s handler form, from third-party sources.
+
+| Middleware | Description | Example |
+| -----------|--------|-------------|
+| [jwt](https://github.com/iris-contrib/middleware/tree/master/jwt) | Middleware checks for a JWT on the `Authorization` header on incoming requests and decodes it. | [iris-contrib/middleware/jwt/_example](https://github.com/iris-contrib/middleware/tree/master/jwt/_example) |
+| [cors](https://github.com/iris-contrib/middleware/tree/master/cors) | HTTP Access Control. | [iris-contrib/middleware/cors/_example](https://github.com/iris-contrib/middleware/tree/master/cors/_example) |
+| [secure](https://github.com/iris-contrib/middleware/tree/master/secure) | Middleware that implements a few quick security wins. | [iris-contrib/middleware/secure/_example](https://github.com/iris-contrib/middleware/tree/master/secure/_example/main.go) |
+| [tollbooth](https://github.com/iris-contrib/middleware/tree/master/tollboothic) | Generic middleware to rate-limit HTTP requests. | [iris-contrib/middleware/tollbooth/_examples/limit-handler](https://github.com/iris-contrib/middleware/tree/master/tollbooth/_examples/limit-handler) |
+| [cloudwatch](https://github.com/iris-contrib/middleware/tree/master/cloudwatch) |  AWS cloudwatch metrics middleware. |[iris-contrib/middleware/cloudwatch/_example](https://github.com/iris-contrib/middleware/tree/master/cloudwatch/_example) |
+| [new relic](https://github.com/iris-contrib/middleware/tree/master/newrelic) | Official [New Relic Go Agent](https://github.com/newrelic/go-agent). | [iris-contrib/middleware/newrelic/_example](https://github.com/iris-contrib/middleware/tree/master/newrelic/_example) |
+| [prometheus](https://github.com/iris-contrib/middleware/tree/master/prometheus)| Easily create metrics endpoint for the [prometheus](http://prometheus.io) instrumentation tool | [iris-contrib/middleware/prometheus/_example](https://github.com/iris-contrib/middleware/tree/master/prometheus/_example) |
+
+Third-Party Handlers
+------------
+
+iris has its own middleware form of `func(ctx context.Context)` but it's also compatible with all `net/http` middleware forms. See [here](https://github.com/kataras/iris/tree/master/_examples/convert-handlers).
+
+Here's a small list of useful third-party handlers:
+
+| Middleware | Description |
+| -----------|-------------|
+| [goth](https://github.com/markbates/goth) | OAuth, OAuth2 authentication. [Example](https://github.com/kataras/iris/tree/master/_examples/authentication/oauth2) |
+| [binding](https://github.com/mholt/binding) | Data binding from HTTP requests into structs |
+| [csp](https://github.com/awakenetworks/csp) | [Content Security Policy](https://www.w3.org/TR/CSP2/) (CSP) support |
+| [delay](https://github.com/jeffbmartinez/delay) | Add delays/latency to endpoints. Useful when testing effects of high latency |
+| [onthefly](https://github.com/xyproto/onthefly) | Generate TinySVG, HTML and CSS on the fly |
+| [permissions2](https://github.com/xyproto/permissions2) | Cookies, users and permissions |
+| [RestGate](https://github.com/pjebs/restgate) | Secure authentication for REST API endpoints |
+| [stats](https://github.com/thoas/stats) | Store information about your web application (response time, etc.) |
+| [VanGoH](https://github.com/auroratechnologies/vangoh) | Configurable [AWS-Style](http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html) HMAC authentication middleware |
+| [xrequestid](https://github.com/pilu/xrequestid) | Middleware that assigns a random X-Request-Id header to each request |
+| [digits](https://github.com/bamarni/digits) | Middleware that handles [Twitter Digits](https://get.digits.com/) authentication |
+
+
+> Feel free to put up your own middleware in this list!
\ No newline at end of file
diff --git a/middleware/basicauth/basicauth.go b/middleware/basicauth/basicauth.go
index b3c04112..303283bb 100644
--- a/middleware/basicauth/basicauth.go
+++ b/middleware/basicauth/basicauth.go
@@ -1,10 +1,8 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package basicauth provides http basic authentication via middleware. See _examples/beginner/basicauth
+// Package basicauth provides http basic authentication via middleware. See _examples/authentication/basicauth
 package basicauth
 
+// test file: ../../_examples/authentication/basicauth/main_test.go
+
 import (
 	"encoding/base64"
 	"strconv"
diff --git a/middleware/basicauth/basicauth_test.go b/middleware/basicauth/basicauth_test.go
deleted file mode 100644
index ebfa4595..00000000
--- a/middleware/basicauth/basicauth_test.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// black-box testing
-package basicauth_test
-
-import (
-	"testing"
-
-	"github.com/kataras/iris"
-	"github.com/kataras/iris/context"
-	"github.com/kataras/iris/httptest"
-	"github.com/kataras/iris/middleware/basicauth"
-)
-
-func buildApp() *iris.Application {
-	app := iris.New()
-
-	authConfig := basicauth.Config{
-		Users: map[string]string{"myusername": "mypassword"},
-	}
-
-	authentication := basicauth.New(authConfig)
-
-	app.Get("/", func(ctx context.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 context.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 TestBasicAuth(t *testing.T) {
-	app := buildApp()
-	e := httptest.New(t, app)
-
-	// redirects to /admin without basic auth
-	e.GET("/").Expect().Status(iris.StatusUnauthorized)
-	// without basic auth
-	e.GET("/admin").Expect().Status(iris.StatusUnauthorized)
-
-	// with valid basic auth
-	e.GET("/admin").WithBasicAuth("myusername", "mypassword").Expect().
-		Status(iris.StatusOK).Body().Equal("/admin myusername:mypassword")
-	e.GET("/admin/profile").WithBasicAuth("myusername", "mypassword").Expect().
-		Status(iris.StatusOK).Body().Equal("/admin/profile myusername:mypassword")
-	e.GET("/admin/settings").WithBasicAuth("myusername", "mypassword").Expect().
-		Status(iris.StatusOK).Body().Equal("/admin/settings myusername:mypassword")
-
-	// with invalid basic auth
-	e.GET("/admin/settings").WithBasicAuth("invalidusername", "invalidpassword").
-		Expect().Status(iris.StatusUnauthorized)
-}
diff --git a/middleware/basicauth/config.go b/middleware/basicauth/config.go
index 59473ce1..3d0cc807 100644
--- a/middleware/basicauth/config.go
+++ b/middleware/basicauth/config.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package basicauth
 
 import (
diff --git a/middleware/i18n/i18n.go b/middleware/i18n/i18n.go
index 1c79533a..da296387 100644
--- a/middleware/i18n/i18n.go
+++ b/middleware/i18n/i18n.go
@@ -1,4 +1,4 @@
-// Package i18n provides internalization and localization via middleware. See _examples/intermediate/i18n
+// Package i18n provides internalization and localization via middleware. See _examples/miscellaneous/i18n
 package i18n
 
 import (
@@ -9,6 +9,7 @@ import (
 	"github.com/kataras/iris/context"
 )
 
+// test file: ../../_examples/miscellaneous/i18n/main_test.go
 type i18nMiddleware struct {
 	config Config
 }
@@ -23,7 +24,6 @@ func (i *i18nMiddleware) ServeHTTP(ctx context.Context) {
 	if ctx.Values().GetString(langKey) == "" {
 		// try to get by url parameter
 		language = ctx.URLParam(i.config.URLParameter)
-
 		if language == "" {
 			// then try to take the lang field from the cookie
 			language = ctx.GetCookie(langKey)
@@ -44,9 +44,16 @@ func (i *i18nMiddleware) ServeHTTP(ctx context.Context) {
 		if language == "" {
 			language = i.config.Default
 		}
-		ctx.Values().Set(langKey, language)
 	}
+
 	locale := i18n.Locale{Lang: language}
+
+	// if unexpected language given, the middleware will  transtlate to the default language, the language key should be
+	// also this language instead of the user-given
+	if indexLang := locale.Index(); indexLang == -1 {
+		locale.Lang = i.config.Default
+	}
+	ctx.Values().Set(langKey, locale.Lang)
 	translateFuncKey := ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey()
 	ctx.Values().Set(translateFuncKey, locale.Tr)
 	ctx.Next()
@@ -73,13 +80,13 @@ func New(c Config) context.Handler {
 		}
 		err := i18n.SetMessage(k, v)
 		if err != nil && err != i18n.ErrLangAlreadyExist {
-			panic("Iris i18n Middleware: Failed to set locale file" + k + " Error:" + err.Error())
+			panic("iris i18n Middleware: Failed to set locale file" + k + " Error:" + err.Error())
 		}
 		if firstlanguage == "" {
 			firstlanguage = k
 		}
 	}
-	// if not default language setted then set to the first of the i.options.Languages
+	// if not default language setted then set to the first of the i.config.Languages
 	if c.Default == "" {
 		c.Default = firstlanguage
 	}
@@ -88,8 +95,8 @@ func New(c Config) context.Handler {
 	return i.ServeHTTP
 }
 
-// TranslatedMap returns translated map[string]interface{} from i18n structure
-func TranslatedMap(sourceInterface interface{}, ctx context.Context) map[string]interface{} {
+// TranslatedMap returns translated map[string]interface{} from i18n structure.
+func TranslatedMap(ctx context.Context, sourceInterface interface{}) map[string]interface{} {
 	iType := reflect.TypeOf(sourceInterface).Elem()
 	result := make(map[string]interface{})
 
diff --git a/middleware/logger/config.go b/middleware/logger/config.go
index cfe8bca3..c6592a6a 100644
--- a/middleware/logger/config.go
+++ b/middleware/logger/config.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package logger
 
 // Config are the options of the logger middlweare
diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go
index c6591ae9..d90edb16 100644
--- a/middleware/logger/logger.go
+++ b/middleware/logger/logger.go
@@ -1,8 +1,4 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package logger provides request logging via middleware. See _examples/beginner/request-logger
+// Package logger provides request logging via middleware. See _examples/http_request/request-logger
 package logger
 
 import (
@@ -49,7 +45,7 @@ func (l *requestLoggerMiddleware) ServeHTTP(ctx context.Context) {
 	}
 
 	//finally print the logs, no new line, the framework's logger is responsible how to render each log.
-	ctx.Application().Log("%v %4v %s %s %s", status, latency, ip, method, path)
+	ctx.Application().Logger().Infof("%v %4v %s %s %s", status, latency, ip, method, path)
 }
 
 // New creates and returns a new request logger middleware.
diff --git a/middleware/pprof/pprof.go b/middleware/pprof/pprof.go
index 91a03f59..03f1b0f7 100644
--- a/middleware/pprof/pprof.go
+++ b/middleware/pprof/pprof.go
@@ -1,8 +1,4 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package pprof provides native pprof support via middleware. See _examples/beginner/pprof
+// Package pprof provides native pprof support via middleware. See _examples/miscellaneous/pprof
 package pprof
 
 import (
diff --git a/middleware/recover/recover.go b/middleware/recover/recover.go
index c5ff3457..f725178a 100644
--- a/middleware/recover/recover.go
+++ b/middleware/recover/recover.go
@@ -1,8 +1,4 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package recover provides recovery for specific routes or for the whole app via middleware. See _examples/beginner/recover
+// Package recover provides recovery for specific routes or for the whole app via middleware. See _examples/miscellaneous/recover
 package recover
 
 import (
@@ -23,9 +19,9 @@ func getRequestLogs(ctx context.Context) string {
 	return fmt.Sprintf("%v %s %s %s", status, path, method, ip)
 }
 
-// New returns a new recover middleware
-// it logs to the LoggerOut iris' configuration field if its IsDeveloper configuration field is enabled.
-// otherwise it just continues to serve
+// New returns a new recover middleware,
+// it recovers from panics and logs
+// the panic message to the application's logger "Warn" level.
 func New() context.Handler {
 	return func(ctx context.Context) {
 		defer func() {
@@ -49,12 +45,11 @@ func New() context.Handler {
 				logMessage := fmt.Sprintf("Recovered from a route's Handler('%s')\n", ctx.HandlerName())
 				logMessage += fmt.Sprintf("At Request: %s\n", getRequestLogs(ctx))
 				logMessage += fmt.Sprintf("Trace: %s\n", err)
-				logMessage += fmt.Sprintf("\n%s\n", stacktrace)
-				ctx.Application().Log(logMessage)
+				logMessage += fmt.Sprintf("\n%s", stacktrace)
+				ctx.Application().Logger().Warnln(logMessage)
 
-				ctx.StopExecution()
 				ctx.StatusCode(500)
-
+				ctx.StopExecution()
 			}
 		}()
 
diff --git a/sessions/AUTHORS b/sessions/AUTHORS
new file mode 100644
index 00000000..592f37c5
--- /dev/null
+++ b/sessions/AUTHORS
@@ -0,0 +1,5 @@
+# This is the official list of Iris Sessions authors for copyright
+# purposes.
+
+Gerasimos Maropoulos 
+Bill Qeras, Jr. 
\ No newline at end of file
diff --git a/sessions/LICENSE b/sessions/LICENSE
index 7eb6b6e4..d6467e14 100644
--- a/sessions/LICENSE
+++ b/sessions/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
+Copyright (c) 2017 The Iris Sessions Authors. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -10,8 +10,8 @@ notice, this list of conditions and the following disclaimer.
 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 Gerasimos Maropoulos nor the name of his
-username, kataras, may be used to endorse or promote products derived from
+   * 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
diff --git a/sessions/README.md b/sessions/README.md
new file mode 100644
index 00000000..aa465ee1
--- /dev/null
+++ b/sessions/README.md
@@ -0,0 +1,11 @@
+# Sessions
+
+Fast HTTP Sessions for the [iris](https://github.com/kataras/iris) web framework.
+
+## Table of contents
+
+* [Overview](_examples/overview/main.go)
+* [Standalone](_examples/standalone/main.go)
+* [Secure Cookie](_examples/securecookie/main.go)
+* [Flash Messages](_examples/flash-messages/main.go)
+* [Database](_examples/database/main.go)
\ No newline at end of file
diff --git a/_examples/intermediate/sessions/database/main.go b/sessions/_examples/database/main.go
similarity index 78%
rename from _examples/intermediate/sessions/database/main.go
rename to sessions/_examples/database/main.go
index 2ec4d7b8..831d89e8 100644
--- a/_examples/intermediate/sessions/database/main.go
+++ b/sessions/_examples/database/main.go
@@ -21,17 +21,15 @@ func main() {
 		Prefix:        "",
 		MaxAgeSeconds: service.DefaultRedisMaxAgeSeconds}) // optionally configure the bridge between your redis server
 
-	mySessions := sessions.New(sessions.Config{Cookie: "mysessionid"})
+	sess := sessions.New(sessions.Config{Cookie: "sessionscookieid"})
 
 	//
 	// IMPORTANT:
 	//
-	mySessions.UseDatabase(db)
+	sess.UseDatabase(db)
 
 	// the rest of the code stays the same.
 	app := iris.New()
-	// Attach the session manager we just created
-	app.AttachSessionManager(mySessions)
 
 	app.Get("/", func(ctx context.Context) {
 		ctx.Writef("You should navigate to the /set, /get, /delete, /clear,/destroy instead")
@@ -39,32 +37,32 @@ func main() {
 	app.Get("/set", func(ctx context.Context) {
 
 		//set session values
-		ctx.Session().Set("name", "iris")
+		sess.Start(ctx).Set("name", "iris")
 
 		//test if setted here
-		ctx.Writef("All ok session setted to: %s", ctx.Session().GetString("name"))
+		ctx.Writef("All ok session setted to: %s", sess.Start(ctx).GetString("name"))
 	})
 
 	app.Get("/get", func(ctx context.Context) {
 		// get a specific key, as string, if no found returns just an empty string
-		name := ctx.Session().GetString("name")
+		name := sess.Start(ctx).GetString("name")
 
 		ctx.Writef("The name on the /set was: %s", name)
 	})
 
 	app.Get("/delete", func(ctx context.Context) {
 		// delete a specific key
-		ctx.Session().Delete("name")
+		sess.Start(ctx).Delete("name")
 	})
 
 	app.Get("/clear", func(ctx context.Context) {
 		// removes all entries
-		ctx.Session().Clear()
+		sess.Start(ctx).Clear()
 	})
 
 	app.Get("/destroy", func(ctx context.Context) {
 		//destroy, removes the entire session data and cookie
-		ctx.SessionDestroy()
+		sess.Destroy(ctx)
 	})
 
 	app.Run(iris.Addr(":8080"))
diff --git a/_examples/intermediate/sessions/flash-messages/main.go b/sessions/_examples/flash-messages/main.go
similarity index 69%
rename from _examples/intermediate/sessions/flash-messages/main.go
rename to sessions/_examples/flash-messages/main.go
index 282df5d8..d76854a8 100644
--- a/_examples/intermediate/sessions/flash-messages/main.go
+++ b/sessions/_examples/flash-messages/main.go
@@ -3,23 +3,23 @@ package main
 import (
 	"github.com/kataras/iris"
 	"github.com/kataras/iris/context"
+
 	"github.com/kataras/iris/sessions"
 )
 
 func main() {
 	app := iris.New()
-	// output startup banner and error logs on os.Stdout
-
 	sess := sessions.New(sessions.Config{Cookie: "myappsessionid"})
-	app.AttachSessionManager(sess)
 
 	app.Get("/set", func(ctx context.Context) {
-		ctx.Session().SetFlash("name", "iris")
+		s := sess.Start(ctx)
+		s.SetFlash("name", "iris")
 		ctx.Writef("Message setted, is available for the next request")
 	})
 
 	app.Get("/get", func(ctx context.Context) {
-		name := ctx.Session().GetFlashString("name")
+		s := sess.Start(ctx)
+		name := s.GetFlashString("name")
 		if name == "" {
 			ctx.Writef("Empty name!!")
 			return
@@ -28,15 +28,15 @@ func main() {
 	})
 
 	app.Get("/test", func(ctx context.Context) {
-		name := ctx.Session().GetFlashString("name")
+		s := sess.Start(ctx)
+		name := s.GetFlashString("name")
 		if name == "" {
 			ctx.Writef("Empty name!!")
 			return
 		}
 
-		ctx.Writef("Ok you are comming from /set ,the value of the name is %s", name)
+		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.Run(iris.Addr(":8080"))
diff --git a/_examples/intermediate/sessions/overview/main.go b/sessions/_examples/overview/main.go
similarity index 67%
rename from _examples/intermediate/sessions/overview/main.go
rename to sessions/_examples/overview/main.go
index df8995c5..fde18281 100644
--- a/_examples/intermediate/sessions/overview/main.go
+++ b/sessions/_examples/overview/main.go
@@ -8,13 +8,14 @@ import (
 )
 
 var (
-	key = "my_sessionid"
+	cookieNameForSessionID = "mycookiesessionnameid"
+	sess                   = sessions.New(sessions.Config{Cookie: cookieNameForSessionID})
 )
 
 func secret(ctx context.Context) {
 
 	// Check if user is authenticated
-	if auth, _ := ctx.Session().GetBoolean("authenticated"); !auth {
+	if auth, _ := sess.Start(ctx).GetBoolean("authenticated"); !auth {
 		ctx.StatusCode(iris.StatusForbidden)
 		return
 	}
@@ -24,7 +25,7 @@ func secret(ctx context.Context) {
 }
 
 func login(ctx context.Context) {
-	session := ctx.Session()
+	session := sess.Start(ctx)
 
 	// Authentication goes here
 	// ...
@@ -34,7 +35,7 @@ func login(ctx context.Context) {
 }
 
 func logout(ctx context.Context) {
-	session := ctx.Session()
+	session := sess.Start(ctx)
 
 	// Revoke users authentication
 	session.Set("authenticated", false)
@@ -43,11 +44,6 @@ func logout(ctx context.Context) {
 func main() {
 	app := iris.New()
 
-	// Look https://github.com/kataras/iris/tree/master/sessions/_examples for more features,
-	// i.e encode/decode and lifetime.
-	sess := sessions.New(sessions.Config{Cookie: key})
-	app.AttachSessionManager(sess)
-
 	app.Get("/secret", secret)
 	app.Get("/login", login)
 	app.Get("/logout", logout)
diff --git a/_examples/intermediate/sessions/securecookie/main.go b/sessions/_examples/securecookie/main.go
similarity index 76%
rename from _examples/intermediate/sessions/securecookie/main.go
rename to sessions/_examples/securecookie/main.go
index 83ff3d1f..c961c538 100644
--- a/_examples/intermediate/sessions/securecookie/main.go
+++ b/sessions/_examples/securecookie/main.go
@@ -8,6 +8,7 @@ package main
 import (
 	"github.com/kataras/iris"
 	"github.com/kataras/iris/context"
+
 	"github.com/kataras/iris/sessions"
 
 	"github.com/gorilla/securecookie"
@@ -29,45 +30,41 @@ func main() {
 		Decode: secureCookie.Decode,
 	})
 
-	// OPTIONALLY:
-	// import "github.com/kataras/iris/sessions/sessiondb/redis"
-	// or import "github.com/kataras/go-sessions/sessiondb/$any_available_community_database"
-	// mySessions.UseDatabase(redis.New(...))
-
-	app.AttachSessionManager(mySessions) // Attach the session manager we just created.
-
 	app.Get("/", func(ctx context.Context) {
 		ctx.Writef("You should navigate to the /set, /get, /delete, /clear,/destroy instead")
 	})
 	app.Get("/set", func(ctx context.Context) {
 
 		//set session values
-		ctx.Session().Set("name", "iris")
+		s := mySessions.Start(ctx)
+		s.Set("name", "iris")
 
 		//test if setted here
-		ctx.Writef("All ok session setted to: %s", ctx.Session().GetString("name"))
+		ctx.Writef("All ok session setted to: %s", s.GetString("name"))
 	})
 
 	app.Get("/get", func(ctx context.Context) {
 		// get a specific key, as string, if no found returns just an empty string
-		name := ctx.Session().GetString("name")
+		s := mySessions.Start(ctx)
+		name := s.GetString("name")
 
 		ctx.Writef("The name on the /set was: %s", name)
 	})
 
 	app.Get("/delete", func(ctx context.Context) {
 		// delete a specific key
-		ctx.Session().Delete("name")
+		s := mySessions.Start(ctx)
+		s.Delete("name")
 	})
 
 	app.Get("/clear", func(ctx context.Context) {
 		// removes all entries
-		ctx.Session().Clear()
+		mySessions.Start(ctx).Clear()
 	})
 
 	app.Get("/destroy", func(ctx context.Context) {
 		//destroy, removes the entire session data and cookie
-		ctx.SessionDestroy()
+		mySessions.Destroy(ctx)
 	}) // Note about destroy:
 	//
 	// You can destroy a session outside of a handler too, using the:
diff --git a/sessions/_examples/securecookie/main_test.go b/sessions/_examples/securecookie/main_test.go
new file mode 100644
index 00000000..c0f140a4
--- /dev/null
+++ b/sessions/_examples/securecookie/main_test.go
@@ -0,0 +1,105 @@
+package main
+
+import (
+	"testing"
+
+	"github.com/kataras/iris"
+	"github.com/kataras/iris/context"
+	"github.com/kataras/iris/httptest"
+	"github.com/kataras/iris/sessions"
+
+	"github.com/gorilla/securecookie"
+)
+
+func TestSessionsEncodeDecode(t *testing.T) {
+	// test the sessions encode decode via gorilla.securecookie
+	app := iris.New()
+	// IMPORTANT
+	cookieName := "mycustomsessionid"
+	// 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 := []byte("the-big-and-secret-fash-key-here")
+	blockKey := []byte("lot-secret-of-characters-big-too")
+	secureCookie := securecookie.New(hashKey, blockKey)
+	sess := sessions.New(sessions.Config{
+		Cookie: cookieName,
+		Encode: secureCookie.Encode,
+		Decode: secureCookie.Decode,
+	})
+
+	testSessions(t, sess, app)
+}
+
+func testSessions(t *testing.T, sess *sessions.Sessions, app *iris.Application) {
+	values := map[string]interface{}{
+		"Name":   "iris",
+		"Months": "4",
+		"Secret": "dsads£2132215£%%Ssdsa",
+	}
+
+	writeValues := func(ctx context.Context) {
+		s := sess.Start(ctx)
+		sessValues := s.GetAll()
+
+		ctx.JSON(sessValues)
+	}
+
+	app.Post("/set", func(ctx context.Context) {
+		s := sess.Start(ctx)
+		vals := make(map[string]interface{}, 0)
+		if err := ctx.ReadJSON(&vals); err != nil {
+			t.Fatalf("Cannot readjson. 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) {
+		sess.Start(ctx).Clear()
+		writeValues(ctx)
+	})
+
+	app.Get("/destroy", func(ctx context.Context) {
+		sess.Destroy(ctx)
+		writeValues(ctx)
+		// the cookie and all values should be empty
+	})
+
+	// request cookie should be empty
+	app.Get("/after_destroy", func(ctx context.Context) {
+	})
+
+	app.Get("/multi_start_set_get", func(ctx context.Context) {
+		s := sess.Start(ctx)
+		s.Set("key", "value")
+		ctx.Next()
+	}, func(ctx context.Context) {
+		s := sess.Start(ctx)
+		ctx.Writef(s.GetString("key"))
+	})
+
+	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)
+
+	// test destroy which also clears first
+	d := e.GET("/destroy").Expect().Status(iris.StatusOK)
+	d.JSON().Object().Empty()
+	// 	This removed: d.Cookies().Empty(). Reason:
+	// httpexpect counts the cookies setted or deleted at the response time, but cookie is not removed, to be really removed needs to SetExpire(now-1second) so,
+	// test if the cookies removed on the next request, like the browser's behavior.
+	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()
+
+	// test start on the same request but more than one times
+
+	e.GET("/multi_start_set_get").Expect().Status(iris.StatusOK).Body().Equal("value")
+}
diff --git a/_examples/intermediate/sessions/standalone/main.go b/sessions/_examples/standalone/main.go
similarity index 85%
rename from _examples/intermediate/sessions/standalone/main.go
rename to sessions/_examples/standalone/main.go
index 726cc91e..b26ebf73 100644
--- a/_examples/intermediate/sessions/standalone/main.go
+++ b/sessions/_examples/standalone/main.go
@@ -5,6 +5,7 @@ import (
 
 	"github.com/kataras/iris"
 	"github.com/kataras/iris/context"
+
 	"github.com/kataras/iris/sessions"
 )
 
@@ -14,7 +15,7 @@ type businessModel struct {
 
 func main() {
 	app := iris.New()
-	mySessions := sessions.New(sessions.Config{
+	sess := sessions.New(sessions.Config{
 		// Cookie string, the session's client cookie name, for example: "mysessionid"
 		//
 		// Defaults to "irissessionid"
@@ -30,19 +31,17 @@ func main() {
 		// want to be crazy safe? Take a look at the "securecookie" example folder.
 	})
 
-	app.AttachSessionManager(mySessions) // Attach the session manager we just created.
-
 	app.Get("/", func(ctx context.Context) {
 		ctx.Writef("You should navigate to the /set, /get, /delete, /clear,/destroy instead")
 	})
 	app.Get("/set", func(ctx context.Context) {
 
 		//set session values.
-
-		ctx.Session().Set("name", "iris")
+		s := sess.Start(ctx)
+		s.Set("name", "iris")
 
 		//test if setted here
-		ctx.Writef("All ok session setted to: %s", ctx.Session().GetString("name"))
+		ctx.Writef("All ok session setted to: %s", s.GetString("name"))
 
 		// Set will set the value as-it-is,
 		// if it's a slice or map
@@ -55,25 +54,25 @@ func main() {
 
 	app.Get("/get", func(ctx context.Context) {
 		// get a specific value, as string, if no found returns just an empty string
-		name := ctx.Session().GetString("name")
+		name := sess.Start(ctx).GetString("name")
 
 		ctx.Writef("The name on the /set was: %s", name)
 	})
 
 	app.Get("/delete", func(ctx context.Context) {
 		// delete a specific key
-		ctx.Session().Delete("name")
+		sess.Start(ctx).Delete("name")
 	})
 
 	app.Get("/clear", func(ctx context.Context) {
 		// removes all entries
-		ctx.Session().Clear()
+		sess.Start(ctx).Clear()
 	})
 
 	app.Get("/destroy", func(ctx context.Context) {
 
 		//destroy, removes the entire session data and cookie
-		ctx.SessionDestroy()
+		sess.Destroy(ctx)
 	})
 	// Note about Destroy:
 	//
@@ -89,8 +88,9 @@ func main() {
 	// Read more about muttable and immutable go types: https://stackoverflow.com/a/8021081
 	app.Get("/set_immutable", func(ctx context.Context) {
 		business := []businessModel{{Name: "Edward"}, {Name: "value 2"}}
-		ctx.Session().SetImmutable("businessEdit", business)
-		businessGet := ctx.Session().Get("businessEdit").([]businessModel)
+		s := sess.Start(ctx)
+		s.SetImmutable("businessEdit", business)
+		businessGet := s.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.
@@ -99,7 +99,7 @@ func main() {
 	})
 
 	app.Get("/get_immutable", func(ctx context.Context) {
-		valSlice := ctx.Session().Get("businessEdit")
+		valSlice := sess.Start(ctx).Get("businessEdit")
 		if valSlice == nil {
 			ctx.HTML("please navigate to the /set_immutable first")
 			return
diff --git a/sessions/config.go b/sessions/config.go
index 4d4ab95f..d43c0954 100644
--- a/sessions/config.go
+++ b/sessions/config.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package sessions
 
 import (
@@ -13,8 +9,6 @@ import (
 const (
 	// DefaultCookieName the secret cookie's name for sessions
 	DefaultCookieName = "irissessionid"
-	// DefaultCookieLength is the default Session Manager's CookieLength, which is 32
-	DefaultCookieLength = 32
 )
 
 type (
@@ -73,7 +67,7 @@ type (
 		// that, but developers can change that with simple assignment.
 		SessionIDGenerator func() string
 
-		// DisableSubdomainPersistence set it to true in order dissallow your q subdomains to have access to the session cookie
+		// DisableSubdomainPersistence set it to true in order dissallow your subdomains to have access to the session cookie
 		//
 		// Defaults to false
 		DisableSubdomainPersistence bool
diff --git a/sessions/cookie.go b/sessions/cookie.go
index dd9d9f12..b1942729 100644
--- a/sessions/cookie.go
+++ b/sessions/cookie.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package sessions
 
 import (
@@ -9,6 +5,8 @@ import (
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/kataras/iris/context"
 )
 
 var (
@@ -21,24 +19,27 @@ var (
 
 // GetCookie returns cookie's value by it's name
 // returns empty string if nothing was found
-func GetCookie(name string, req *http.Request) string {
-	c, err := req.Cookie(name)
+func GetCookie(ctx context.Context, name string) string {
+	c, err := ctx.Request().Cookie(name)
 	if err != nil {
 		return ""
 	}
+
 	return c.Value
+
+	// return ctx.GetCookie(name)
 }
 
 // AddCookie adds a cookie
-func AddCookie(cookie *http.Cookie, res http.ResponseWriter) {
-	if v := cookie.String(); v != "" {
-		http.SetCookie(res, cookie)
-	}
+func AddCookie(ctx context.Context, cookie *http.Cookie) {
+	// http.SetCookie(ctx.ResponseWriter(), cookie)
+	// ctx.Request().AddCookie(cookie)
+	ctx.SetCookie(cookie)
 }
 
 // RemoveCookie deletes a cookie by it's name/key
-func RemoveCookie(name string, res http.ResponseWriter, req *http.Request) {
-	c, err := req.Cookie(name)
+func RemoveCookie(ctx context.Context, name string) {
+	c, err := ctx.Request().Cookie(name)
 	if err != nil {
 		return
 	}
@@ -48,7 +49,7 @@ func RemoveCookie(name string, res http.ResponseWriter, req *http.Request) {
 	c.MaxAge = -1
 	c.Value = ""
 	c.Path = "/"
-	AddCookie(c, res)
+	AddCookie(ctx, c)
 }
 
 // IsValidCookieDomain returns true if the receiver is a valid domain to set
diff --git a/sessions/database.go b/sessions/database.go
index 43bd206b..a22eb1a9 100644
--- a/sessions/database.go
+++ b/sessions/database.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package sessions
 
 // Database is the interface which all session databases should implement
@@ -10,7 +6,7 @@ package sessions
 // The scope of the database is to session somewhere the sessions in order to
 //  keep them after restarting the server, nothing more.
 // the values are sessiond by the underline session, the check for new sessions, or
-// 'this session value should added' are made automatically by q, you are able just to set the values to your backend database with Load function.
+// 'this session value should added' are made automatically you are able just to set the values to your backend database with Load function.
 // session database doesn't have any write or read access to the session, the loading of
 // the initial data is done by the Load(string) map[string]interfface{} function
 // synchronization are made automatically, you can register more than one session database
diff --git a/sessions/provider.go b/sessions/provider.go
index 240dd37e..4eb3ce6d 100644
--- a/sessions/provider.go
+++ b/sessions/provider.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package sessions
 
 import (
@@ -16,10 +12,10 @@ type (
 	// It's the session memory manager
 	provider struct {
 		// we don't use RWMutex because all actions have read and write at the same action function.
-		// (or write to a *session's value which is race if we don't lock)
+		// (or write to a *Session's value which is race if we don't lock)
 		// narrow locks are fasters but are useless here.
 		mu        sync.Mutex
-		sessions  map[string]*session
+		sessions  map[string]*Session
 		databases []Database
 	}
 )
@@ -27,7 +23,7 @@ type (
 // newProvider returns a new sessions provider
 func newProvider() *provider {
 	return &provider{
-		sessions:  make(map[string]*session, 0),
+		sessions:  make(map[string]*Session, 0),
 		databases: make([]Database, 0),
 	}
 }
@@ -41,9 +37,8 @@ func (p *provider) RegisterDatabase(db Database) {
 }
 
 // newSession returns a new session from sessionid
-func (p *provider) newSession(sid string, expires time.Duration) *session {
-
-	sess := &session{
+func (p *provider) newSession(sid string, expires time.Duration) *Session {
+	sess := &Session{
 		sid:      sid,
 		provider: p,
 		values:   p.loadSessionValuesFromDB(sid),
@@ -92,7 +87,7 @@ func (p *provider) updateDatabases(sid string, store memstore.Store) {
 }
 
 // Init creates the session  and returns it
-func (p *provider) Init(sid string, expires time.Duration) Session {
+func (p *provider) Init(sid string, expires time.Duration) *Session {
 	newSession := p.newSession(sid, expires)
 	p.mu.Lock()
 	p.sessions[sid] = newSession
@@ -101,7 +96,7 @@ func (p *provider) Init(sid string, expires time.Duration) Session {
 }
 
 // Read returns the store which sid parameter belongs
-func (p *provider) Read(sid string, expires time.Duration) Session {
+func (p *provider) Read(sid string, expires time.Duration) *Session {
 	p.mu.Lock()
 	if sess, found := p.sessions[sid]; found {
 		sess.runFlashGC() // run the flash messages GC, new request here of existing session
diff --git a/sessions/session.go b/sessions/session.go
index 7c42c070..69e78821 100644
--- a/sessions/session.go
+++ b/sessions/session.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package sessions
 
 import (
@@ -14,10 +10,12 @@ import (
 )
 
 type (
-
-	// session is an 'object' which wraps the session provider with its session databases, only frontend user has access to this session object.
-	// implements the context.Session interface
-	session struct {
+	// Session should expose the Sessions's end-user API.
+	// It is the session's storage controller which you can
+	// save or retrieve values based on a key.
+	//
+	// This is what will be returned when sess := sessions.Start().
+	Session struct {
 		sid    string
 		values memstore.Store // here are the real values
 		// we could set the flash messages inside values but this will bring us more problems
@@ -39,15 +37,13 @@ type (
 	}
 )
 
-var _ Session = &session{}
-
-// ID returns the session's id
-func (s *session) ID() string {
+// ID returns the session's ID.
+func (s *Session) ID() string {
 	return s.sid
 }
 
 // Get returns a value based on its "key".
-func (s *session) Get(key string) interface{} {
+func (s *Session) Get(key string) interface{} {
 	s.mu.RLock()
 	value := s.values.Get(key)
 	s.mu.RUnlock()
@@ -55,8 +51,8 @@ func (s *session) Get(key string) interface{} {
 	return value
 }
 
-// when running on the session manager removes any 'old' flash messages
-func (s *session) runFlashGC() {
+// when running on the session manager removes any 'old' flash messages.
+func (s *Session) runFlashGC() {
 	s.mu.Lock()
 	for key, v := range s.flashes {
 		if v.shouldRemove {
@@ -67,7 +63,7 @@ func (s *session) runFlashGC() {
 }
 
 // HasFlash returns true if this session has available flash messages.
-func (s *session) HasFlash() bool {
+func (s *Session) HasFlash() bool {
 	return len(s.flashes) > 0
 }
 
@@ -80,7 +76,7 @@ func (s *session) HasFlash() bool {
 //
 // Fetching a message deletes it from the session.
 // This means that a message is meant to be displayed only on the first page served to the user.
-func (s *session) GetFlash(key string) interface{} {
+func (s *Session) GetFlash(key string) interface{} {
 	fv, ok := s.peekFlashMessage(key)
 	if !ok {
 		return nil
@@ -92,7 +88,7 @@ func (s *session) GetFlash(key string) interface{} {
 // PeekFlash returns a stored flash message based on its "key".
 // Unlike GetFlash, this will keep the message valid for the next requests,
 // until GetFlashes or GetFlash("key").
-func (s *session) PeekFlash(key string) interface{} {
+func (s *Session) PeekFlash(key string) interface{} {
 	fv, ok := s.peekFlashMessage(key)
 	if !ok {
 		return nil
@@ -100,7 +96,7 @@ func (s *session) PeekFlash(key string) interface{} {
 	return fv.value
 }
 
-func (s *session) peekFlashMessage(key string) (*flashMessage, bool) {
+func (s *Session) peekFlashMessage(key string) (*flashMessage, bool) {
 	s.mu.Lock()
 	if fv, found := s.flashes[key]; found {
 		return fv, true
@@ -110,8 +106,8 @@ func (s *session) peekFlashMessage(key string) (*flashMessage, bool) {
 	return nil, false
 }
 
-// GetString same as Get but returns as string, if nil then returns an empty string
-func (s *session) GetString(key string) string {
+// GetString same as Get but returns as string, if nil then returns an empty string.
+func (s *Session) GetString(key string) string {
 	if value := s.Get(key); value != nil {
 		if v, ok := value.(string); ok {
 			return v
@@ -121,8 +117,8 @@ func (s *session) GetString(key string) string {
 	return ""
 }
 
-// GetFlashString same as GetFlash but returns as string, if nil then returns an empty string
-func (s *session) GetFlashString(key string) string {
+// GetFlashString same as GetFlash but returns as string, if nil then returns an empty string.
+func (s *Session) GetFlashString(key string) string {
 	if value := s.GetFlash(key); value != nil {
 		if v, ok := value.(string); ok {
 			return v
@@ -134,8 +130,8 @@ func (s *session) GetFlashString(key string) string {
 
 var errFindParse = errors.New("Unable to find the %s with key: %s. Found? %#v")
 
-// GetInt same as Get but returns as int, if not found then returns -1 and an error
-func (s *session) GetInt(key string) (int, error) {
+// GetInt same as Get but returns as int, if not found then returns -1 and an error.
+func (s *Session) GetInt(key string) (int, error) {
 	v := s.Get(key)
 
 	if vint, ok := v.(int); ok {
@@ -149,8 +145,8 @@ func (s *session) GetInt(key string) (int, error) {
 	return -1, errFindParse.Format("int", key, v)
 }
 
-// GetInt64 same as Get but returns as int64, if not found then returns -1 and an error
-func (s *session) GetInt64(key string) (int64, error) {
+// GetInt64 same as Get but returns as int64, if not found then returns -1 and an error.
+func (s *Session) GetInt64(key string) (int64, error) {
 	v := s.Get(key)
 
 	if vint64, ok := v.(int64); ok {
@@ -169,8 +165,8 @@ func (s *session) GetInt64(key string) (int64, error) {
 
 }
 
-// GetFloat32 same as Get but returns as float32, if not found then returns -1 and an error
-func (s *session) GetFloat32(key string) (float32, error) {
+// GetFloat32 same as Get but returns as float32, if not found then returns -1 and an error.
+func (s *Session) GetFloat32(key string) (float32, error) {
 	v := s.Get(key)
 
 	if vfloat32, ok := v.(float32); ok {
@@ -196,8 +192,8 @@ func (s *session) GetFloat32(key string) (float32, error) {
 	return -1, errFindParse.Format("float32", key, v)
 }
 
-// GetFloat64 same as Get but returns as float64, if not found then returns -1 and an error
-func (s *session) GetFloat64(key string) (float64, error) {
+// GetFloat64 same as Get but returns as float64, if not found then returns -1 and an error.
+func (s *Session) GetFloat64(key string) (float64, error) {
 	v := s.Get(key)
 
 	if vfloat32, ok := v.(float32); ok {
@@ -220,11 +216,11 @@ func (s *session) GetFloat64(key string) (float64, error) {
 }
 
 // GetBoolean same as Get but returns as boolean, if not found then returns -1 and an error
-func (s *session) GetBoolean(key string) (bool, error) {
+func (s *Session) GetBoolean(key string) (bool, error) {
 	v := s.Get(key)
 	// here we could check for "true", "false" and 0 for false and 1 for true
 	// but this may cause unexpected behavior from the developer if they expecting an error
-	// so we just check if bool, if yes then return that bool, otherwise return false and an error
+	// so we just check if bool, if yes then return that bool, otherwise return false and an error.
 	if vb, ok := v.(bool); ok {
 		return vb, nil
 	}
@@ -232,8 +228,8 @@ func (s *session) GetBoolean(key string) (bool, error) {
 	return false, errFindParse.Format("bool", key, v)
 }
 
-// GetAll returns a copy of all session's values
-func (s *session) GetAll() map[string]interface{} {
+// GetAll returns a copy of all session's values.
+func (s *Session) GetAll() map[string]interface{} {
 	items := make(map[string]interface{}, len(s.values))
 	s.mu.RLock()
 	for _, kv := range s.values {
@@ -244,8 +240,8 @@ func (s *session) GetAll() map[string]interface{} {
 }
 
 // GetFlashes returns all flash messages as map[string](key) and interface{} value
-// NOTE: this will cause at remove all current flash messages on the next request of the same user
-func (s *session) GetFlashes() map[string]interface{} {
+// NOTE: this will cause at remove all current flash messages on the next request of the same user.
+func (s *Session) GetFlashes() map[string]interface{} {
 	flashes := make(map[string]interface{}, len(s.flashes))
 	s.mu.Lock()
 	for key, v := range s.flashes {
@@ -257,11 +253,11 @@ func (s *session) GetFlashes() map[string]interface{} {
 }
 
 // VisitAll loop each one entry and calls the callback function func(key,value)
-func (s *session) VisitAll(cb func(k string, v interface{})) {
+func (s *Session) VisitAll(cb func(k string, v interface{})) {
 	s.values.Visit(cb)
 }
 
-func (s *session) set(key string, value interface{}, immutable bool) {
+func (s *Session) set(key string, value interface{}, immutable bool) {
 	s.mu.Lock()
 	if immutable {
 		s.values.SetImmutable(key, value)
@@ -274,7 +270,7 @@ func (s *session) set(key string, value interface{}, immutable bool) {
 }
 
 // Set fills the session with an entry"value", based on its "key".
-func (s *session) Set(key string, value interface{}) {
+func (s *Session) Set(key string, value interface{}) {
 	s.set(key, value, false)
 }
 
@@ -284,7 +280,7 @@ func (s *session) Set(key string, value interface{}) {
 // if the entry was immutable, for your own safety.
 // Use it consistently, it's far slower than `Set`.
 // Read more about muttable and immutable go types: https://stackoverflow.com/a/8021081
-func (s *session) SetImmutable(key string, value interface{}) {
+func (s *Session) SetImmutable(key string, value interface{}) {
 	s.set(key, value, true)
 }
 
@@ -307,8 +303,8 @@ func (s *session) SetImmutable(key string, value interface{}) {
 // SetFlash("success", "Data saved!");
 //
 // In this example we used the key 'success'.
-// If you want to define more than one flash messages, you will have to use different keys
-func (s *session) SetFlash(key string, value interface{}) {
+// If you want to define more than one flash messages, you will have to use different keys.
+func (s *Session) SetFlash(key string, value interface{}) {
 	s.mu.Lock()
 	s.flashes[key] = &flashMessage{value: value}
 	s.mu.Unlock()
@@ -316,7 +312,7 @@ func (s *session) SetFlash(key string, value interface{}) {
 
 // Delete removes an entry by its key,
 // returns true if actually something was removed.
-func (s *session) Delete(key string) bool {
+func (s *Session) Delete(key string) bool {
 	s.mu.Lock()
 	removed := s.values.Remove(key)
 	s.mu.Unlock()
@@ -325,19 +321,19 @@ func (s *session) Delete(key string) bool {
 	return removed
 }
 
-func (s *session) updateDatabases() {
+func (s *Session) updateDatabases() {
 	s.provider.updateDatabases(s.sid, s.values)
 }
 
-// DeleteFlash removes a flash message by its key
-func (s *session) DeleteFlash(key string) {
+// DeleteFlash removes a flash message by its key.
+func (s *Session) DeleteFlash(key string) {
 	s.mu.Lock()
 	delete(s.flashes, key)
 	s.mu.Unlock()
 }
 
-// Clear removes all entries
-func (s *session) Clear() {
+// Clear removes all entries.
+func (s *Session) Clear() {
 	s.mu.Lock()
 	s.values.Reset()
 	s.mu.Unlock()
@@ -345,8 +341,8 @@ func (s *session) Clear() {
 	s.updateDatabases()
 }
 
-// Clear removes all flash messages
-func (s *session) ClearFlashes() {
+// ClearFlashes removes all flash messages.
+func (s *Session) ClearFlashes() {
 	s.mu.Lock()
 	for key := range s.flashes {
 		delete(s.flashes, key)
diff --git a/sessions/sessiondb/README.md b/sessions/sessiondb/README.md
deleted file mode 100644
index 0180e544..00000000
--- a/sessions/sessiondb/README.md
+++ /dev/null
@@ -1,29 +0,0 @@
-## Session databases
-
-Find more databases at [github.com/kataras/go-sessions/sessiondb](https://github.com/kataras/go-sessions/tree/master/sessiondb).
-
-This folder contains only the redis database because the rest (two so far, 'file' and 'leveldb') were created by the Community.
-So go [there](https://github.com/kataras/go-sessions/tree/master/sessiondb) and find more about them. `Database` is just an
-interface so you're able to `UseDatabase(anyCompatibleDatabase)`. A Database should implement two functions, `Load` and `Update`.
-
-**Database interface**
-
-```go
-type Database interface {
-	Load(string) map[string]interface{}
-	Update(string, map[string]interface{})
-}
-```
-
-```go
-import (
-  "...myDatabase"
-)
-s := New(...)
-s.UseDatabase(myDatabase) // <---
-
-app := iris.New()
-app.Adapt(s)
-
-app.Listen(":8080")
-```
diff --git a/sessions/sessiondb/redis/database.go b/sessions/sessiondb/redis/database.go
index 242c90f1..97386927 100644
--- a/sessions/sessiondb/redis/database.go
+++ b/sessions/sessiondb/redis/database.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package redis
 
 import (
@@ -11,22 +7,22 @@ import (
 	"github.com/kataras/iris/sessions/sessiondb/redis/service"
 )
 
-// Database the redis database for q sessions
+// Database the redis back-end session database for the sessions.
 type Database struct {
 	redis *service.Service
 }
 
-// New returns a new redis database
+// New returns a new redis database.
 func New(cfg ...service.Config) *Database {
 	return &Database{redis: service.New(cfg...)}
 }
 
-// Config returns the configuration for the redis server bridge, you can change them
+// Config returns the configuration for the redis server bridge, you can change them.
 func (d *Database) Config() *service.Config {
 	return d.redis.Config
 }
 
-// Load loads the values to the underline
+// Load loads the values to the underline.
 func (d *Database) Load(sid string) map[string]interface{} {
 	values := make(map[string]interface{})
 
diff --git a/sessions/sessiondb/redis/service/config.go b/sessions/sessiondb/redis/service/config.go
index d43a008e..e71d7ccb 100644
--- a/sessions/sessiondb/redis/service/config.go
+++ b/sessions/sessiondb/redis/service/config.go
@@ -1,13 +1,7 @@
-// Copyright 2017 Gerasimos Maropoulos. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package service
 
 import (
 	"time"
-
-	"github.com/imdario/mergo"
 )
 
 const (
@@ -43,7 +37,7 @@ type Config struct {
 	MaxAgeSeconds int
 }
 
-// DefaultConfig returns the default configuration for Redis service
+// DefaultConfig returns the default configuration for Redis service.
 func DefaultConfig() Config {
 	return Config{
 		Network:       DefaultRedisNetwork,
@@ -57,26 +51,3 @@ func DefaultConfig() Config {
 		MaxAgeSeconds: DefaultRedisMaxAgeSeconds,
 	}
 }
-
-// Merge merges the default with the given config and returns the result
-func (c Config) Merge(cfg []Config) (config Config) {
-
-	if len(cfg) > 0 {
-		config = cfg[0]
-		mergo.Merge(&config, c)
-	} else {
-		_default := c
-		config = _default
-	}
-
-	return
-}
-
-// MergeSingle merges the default with the given config and returns the result
-func (c Config) MergeSingle(cfg Config) (config Config) {
-
-	config = cfg
-	mergo.Merge(&config, c)
-
-	return
-}
diff --git a/sessions/sessiondb/redis/service/service.go b/sessions/sessiondb/redis/service/service.go
index f33ef69e..6c224afb 100644
--- a/sessions/sessiondb/redis/service/service.go
+++ b/sessions/sessiondb/redis/service/service.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package service
 
 import (
@@ -268,9 +264,12 @@ func (r *Service) Connect() {
 }
 
 // New returns a Redis service filled by the passed config
-// to connect call the .Connect()
+// to connect call the .Connect().
 func New(cfg ...Config) *Service {
-	c := DefaultConfig().Merge(cfg)
+	c := DefaultConfig()
+	if len(cfg) > 0 {
+		c = cfg[0]
+	}
 	r := &Service{pool: &redis.Pool{}, Config: &c}
 	return r
 }
diff --git a/sessions/sessions.go b/sessions/sessions.go
index d982b355..db6ae975 100644
--- a/sessions/sessions.go
+++ b/sessions/sessions.go
@@ -1,94 +1,48 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package sessions
 
 import (
 	"net/http"
 	"strings"
 	"time"
+
+	"github.com/kataras/iris/context"
 )
 
-type (
-
-	// Sessions must be implemented within a session manager.
-	//
-	// A Sessions should be responsible to Start a sesion based
-	// on raw http.ResponseWriter and http.Request, which should return
-	// a compatible Session interface, type. If the external session manager
-	// doesn't qualifies, then the user should code the rest of the functions with empty implementation.
-	//
-	// Sessions should be responsible to Destroy a session based
-	// on the http.ResponseWriter and http.Request, this function should works individually.
-	Sessions interface {
-		// Start should start the session for the particular net/http request.
-		Start(http.ResponseWriter, *http.Request) Session
-
-		// Destroy should kills the net/http session and remove the associated cookie.
-		Destroy(http.ResponseWriter, *http.Request)
-	} // Sessions is being implemented by Manager
-
-	// Session should expose the Sessions's end-user API.
-	// This will be returned at the sess := context.Session().
-	Session interface {
-		ID() string
-		Get(string) interface{}
-		HasFlash() bool
-		GetFlash(string) interface{}
-		GetString(key string) string
-		GetFlashString(string) string
-		GetInt(key string) (int, error)
-		GetInt64(key string) (int64, error)
-		GetFloat32(key string) (float32, error)
-		GetFloat64(key string) (float64, error)
-		GetBoolean(key string) (bool, error)
-		GetAll() map[string]interface{}
-		GetFlashes() map[string]interface{}
-		VisitAll(cb func(k string, v interface{}))
-		Set(string, interface{})
-		SetImmutable(key string, value interface{})
-		SetFlash(string, interface{})
-		Delete(string) bool
-		DeleteFlash(string)
-		Clear()
-		ClearFlashes()
-	} // Session is being implemented inside session.go
-
-	// Manager implements the Sessions interface which Iris uses to start and destroy a session from its Context.
-	Manager struct {
-		config   Config
-		provider *provider
-	}
-)
+// A Sessions manager should be responsible to Start a sesion, based
+// on a Context, which should return
+// a compatible Session interface, type. If the external session manager
+// doesn't qualifies, then the user should code the rest of the functions with empty implementation.
+//
+// Sessions should be responsible to Destroy a session based
+// on the Context.
+type Sessions struct {
+	config   Config
+	provider *provider
+}
 
 // New returns a new fast, feature-rich sessions manager
-// it can be adapted to an Iris station
-func New(cfg Config) *Manager {
-	return &Manager{
+// it can be adapted to an iris station
+func New(cfg Config) *Sessions {
+	return &Sessions{
 		config:   cfg.Validate(),
 		provider: newProvider(),
 	}
 }
 
-var _ Sessions = &Manager{}
-
 // UseDatabase adds a session database to the manager's provider,
 // a session db doesn't have write access
-func (s *Manager) UseDatabase(db Database) {
+func (s *Sessions) UseDatabase(db Database) {
 	s.provider.RegisterDatabase(db)
 }
 
-// Start starts the session for the particular net/http request
-func (s *Manager) Start(res http.ResponseWriter, req *http.Request) Session {
-	var sess Session
-
-	cookieValue := GetCookie(s.config.Cookie, req)
+// Start should start the session for the particular request.
+func (s *Sessions) Start(ctx context.Context) *Session {
+	cookieValue := GetCookie(ctx, s.config.Cookie)
 
 	if cookieValue == "" { // cookie doesn't exists, let's generate a session and add set a cookie
 		sid := s.config.SessionIDGenerator()
 
-		sess = s.provider.Init(sid, s.config.Expires)
+		sess := s.provider.Init(sid, s.config.Expires)
 		cookie := &http.Cookie{}
 
 		// The RFC makes no mention of encoding url value, so here I think to encode both sessionid key and the value using the safe(to put and to use as cookie) url-encoding
@@ -98,7 +52,7 @@ func (s *Manager) Start(res http.ResponseWriter, req *http.Request) Session {
 		cookie.Path = "/"
 		if !s.config.DisableSubdomainPersistence {
 
-			requestDomain := req.URL.Host
+			requestDomain := ctx.Request().URL.Host
 			if portIdx := strings.IndexByte(requestDomain, ':'); portIdx > 0 {
 				requestDomain = requestDomain[0:portIdx]
 			}
@@ -125,8 +79,8 @@ func (s *Manager) Start(res http.ResponseWriter, req *http.Request) Session {
 				// finally set the .localhost.com (for(1-level) || .mysubdomain.localhost.com (for 2-level subdomain allow)
 				cookie.Domain = "." + requestDomain // . to allow persistence
 			}
-
 		}
+
 		cookie.HttpOnly = true
 		// MaxAge=0 means no 'Max-Age' attribute specified.
 		// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
@@ -142,33 +96,33 @@ func (s *Manager) Start(res http.ResponseWriter, req *http.Request) Session {
 
 		// set the cookie to secure if this is a tls wrapped request
 		// and the configuration allows it.
-		if req.TLS != nil && s.config.CookieSecureTLS {
+		if ctx.Request().TLS != nil && s.config.CookieSecureTLS {
 			cookie.Secure = true
 		}
 
 		// encode the session id cookie client value right before send it.
 		cookie.Value = s.encodeCookieValue(cookie.Value)
+		AddCookie(ctx, cookie)
 
-		AddCookie(cookie, res)
-	} else {
-
-		cookieValue = s.decodeCookieValue(cookieValue)
-
-		sess = s.provider.Read(cookieValue, s.config.Expires)
+		return sess
 	}
+
+	cookieValue = s.decodeCookieValue(cookieValue)
+	sess := s.provider.Read(cookieValue, s.config.Expires)
 	return sess
+
 }
 
 // Destroy remove the session data and remove the associated cookie.
-func (s *Manager) Destroy(res http.ResponseWriter, req *http.Request) {
-	cookieValue := GetCookie(s.config.Cookie, req)
+func (s *Sessions) Destroy(ctx context.Context) {
+	cookieValue := GetCookie(ctx, s.config.Cookie)
 	// decode the client's cookie value in order to find the server's session id
 	// to destroy the session data.
 	cookieValue = s.decodeCookieValue(cookieValue)
 	if cookieValue == "" { // nothing to destroy
 		return
 	}
-	RemoveCookie(s.config.Cookie, res, req)
+	RemoveCookie(ctx, s.config.Cookie)
 
 	s.provider.Destroy(cookieValue)
 }
@@ -181,19 +135,19 @@ func (s *Manager) Destroy(res http.ResponseWriter, req *http.Request) {
 //
 // Note: the sid should be the original one (i.e: fetched by a store )
 // it's not decoded.
-func (s *Manager) DestroyByID(sid string) {
+func (s *Sessions) DestroyByID(sid string) {
 	s.provider.Destroy(sid)
 }
 
 // DestroyAll removes all sessions
 // from the server-side memory (and database if registered).
 // Client's session cookie will still exist but it will be reseted on the next request.
-func (s *Manager) DestroyAll() {
+func (s *Sessions) DestroyAll() {
 	s.provider.DestroyAll()
 }
 
 // let's keep these funcs simple, we can do it with two lines but we may add more things in the future.
-func (s *Manager) decodeCookieValue(cookieValue string) string {
+func (s *Sessions) decodeCookieValue(cookieValue string) string {
 	var cookieValueDecoded *string
 	if decode := s.config.Decode; decode != nil {
 		err := decode(s.config.Cookie, cookieValue, &cookieValueDecoded)
@@ -206,7 +160,7 @@ func (s *Manager) decodeCookieValue(cookieValue string) string {
 	return cookieValue
 }
 
-func (s *Manager) encodeCookieValue(cookieValue string) string {
+func (s *Sessions) encodeCookieValue(cookieValue string) string {
 	if encode := s.config.Encode; encode != nil {
 		newVal, err := encode(s.config.Cookie, cookieValue)
 		if err == nil {
diff --git a/sessions/sessions_test.go b/sessions/sessions_test.go
new file mode 100644
index 00000000..9457d5a0
--- /dev/null
+++ b/sessions/sessions_test.go
@@ -0,0 +1,194 @@
+package sessions_test
+
+import (
+	"testing"
+
+	"github.com/kataras/iris"
+	"github.com/kataras/iris/context"
+	"github.com/kataras/iris/httptest"
+	"github.com/kataras/iris/sessions"
+)
+
+func TestSessions(t *testing.T) {
+	app := iris.New()
+
+	sess := sessions.New(sessions.Config{Cookie: "mycustomsessionid"})
+	testSessions(t, sess, app)
+}
+
+const (
+	testEnableSubdomain = false
+)
+
+func testSessions(t *testing.T, sess *sessions.Sessions, app *iris.Application) {
+	values := map[string]interface{}{
+		"Name":   "iris",
+		"Months": "4",
+		"Secret": "dsads£2132215£%%Ssdsa",
+	}
+
+	writeValues := func(ctx context.Context) {
+		s := sess.Start(ctx)
+		sessValues := s.GetAll()
+
+		ctx.JSON(sessValues)
+	}
+
+	if testEnableSubdomain {
+		app.Party("subdomain.").Get("/get", func(ctx context.Context) {
+			writeValues(ctx)
+		})
+	}
+
+	app.Post("/set", func(ctx context.Context) {
+		s := sess.Start(ctx)
+		vals := make(map[string]interface{}, 0)
+		if err := ctx.ReadJSON(&vals); err != nil {
+			t.Fatalf("Cannot readjson. 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) {
+		sess.Start(ctx).Clear()
+		writeValues(ctx)
+	})
+
+	app.Get("/destroy", func(ctx context.Context) {
+		sess.Destroy(ctx)
+		writeValues(ctx)
+		// the cookie and all values should be empty
+	})
+
+	// request cookie should be empty
+	app.Get("/after_destroy", func(ctx context.Context) {
+	})
+
+	app.Get("/multi_start_set_get", func(ctx context.Context) {
+		s := sess.Start(ctx)
+		s.Set("key", "value")
+		ctx.Next()
+	}, func(ctx context.Context) {
+		s := sess.Start(ctx)
+		ctx.Writef(s.GetString("key"))
+	})
+
+	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 := httptest.New(t, app, httptest.URL("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()
+	// 	This removed: d.Cookies().Empty(). Reason:
+	// httpexpect counts the cookies setted or deleted at the response time, but cookie is not removed, to be really removed needs to SetExpire(now-1second) so,
+	// test if the cookies removed on the next request, like the browser's behavior.
+	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()
+
+	// 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{}, 0)
+		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)
+}
diff --git a/typescript/AUTHORS b/typescript/AUTHORS
new file mode 100644
index 00000000..dcc4141b
--- /dev/null
+++ b/typescript/AUTHORS
@@ -0,0 +1,5 @@
+# This is the official list of Iris Typescript authors for copyright
+# purposes.
+
+Gerasimos Maropoulos 
+Bill Qeras, Jr. 
\ No newline at end of file
diff --git a/typescript/LICENSE b/typescript/LICENSE
index f8753854..4c27c4c5 100644
--- a/typescript/LICENSE
+++ b/typescript/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
+Copyright (c) 2017 The Iris Typescript Authors. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -10,8 +10,8 @@ notice, this list of conditions and the following disclaimer.
 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 Gerasimos Maropoulos nor the name of his
-username, kataras, may be used to endorse or promote products derived from
+   * 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
@@ -24,4 +24,4 @@ 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.
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/typescript/README.md b/typescript/README.md
new file mode 100644
index 00000000..1b4ff9dd
--- /dev/null
+++ b/typescript/README.md
@@ -0,0 +1,9 @@
+# Typescript
+
+[Typescript](http://www.typescriptlang.org/) and [alm-tools cloud editor](http://alm.tools/) automation tools for the [iris](https://github.com/kataras/iris) web framework.
+
+
+## Table of contents
+
+* [Typescript compiler](_examples/typescript/main.go)
+* [Alm-tools cloud editor](_examples/editor/main.go)
\ No newline at end of file
diff --git a/typescript/_examples/editor/main.go b/typescript/_examples/editor/main.go
new file mode 100644
index 00000000..53ef1ed5
--- /dev/null
+++ b/typescript/_examples/editor/main.go
@@ -0,0 +1,34 @@
+package main
+
+import (
+	"github.com/kataras/iris"
+	"github.com/kataras/iris/context"
+
+	"github.com/kataras/iris/typescript/editor"
+)
+
+func main() {
+	app := iris.New()
+	app.StaticWeb("/scripts", "./www/scripts") // serve the scripts
+	// when you edit a typescript file from the alm-tools
+	// it compiles it to javascript, have fun!
+
+	app.Get("/", func(ctx context.Context) {
+		ctx.ServeFile("./www/index.html", false)
+	})
+
+	editorConfig := editor.Config{
+		Hostname:   "localhost",
+		Port:       4444,
+		WorkingDir: "./www/scripts/", // "/path/to/the/client/side/directory/",
+		Username:   "myusername",
+		Password:   "mypassword",
+	}
+	e := editor.New(editorConfig)
+	e.Run(app.Logger().Infof) // start the editor's server
+
+	// http://localhost:8080
+	// http://localhost:4444
+	app.Run(iris.Addr(":8080"))
+	e.Stop()
+}
diff --git a/_examples/intermediate/cloud-editor/www/index.html b/typescript/_examples/editor/www/index.html
similarity index 69%
rename from _examples/intermediate/cloud-editor/www/index.html
rename to typescript/_examples/editor/www/index.html
index 92bb58ae..72e7d698 100644
--- a/_examples/intermediate/cloud-editor/www/index.html
+++ b/typescript/_examples/editor/www/index.html
@@ -1,6 +1,6 @@
 
 
-Load my script (lawl)
+Load my script
 
 
 	
diff --git a/_examples/intermediate/cloud-editor/www/scripts/app.ts b/typescript/_examples/editor/www/scripts/app.ts
similarity index 83%
rename from _examples/intermediate/cloud-editor/www/scripts/app.ts
rename to typescript/_examples/editor/www/scripts/app.ts
index cc750583..5d51044e 100644
--- a/_examples/intermediate/cloud-editor/www/scripts/app.ts
+++ b/typescript/_examples/editor/www/scripts/app.ts
@@ -11,6 +11,6 @@ class User{
 
 }
 
-var user = new User("kataras");
+var user = new User("iris web framework!");
 var hi = user.Hi("Hello");
 window.alert(hi);
diff --git a/_examples/intermediate/cloud-editor/www/scripts/tsconfig.json b/typescript/_examples/editor/www/scripts/tsconfig.json
similarity index 100%
rename from _examples/intermediate/cloud-editor/www/scripts/tsconfig.json
rename to typescript/_examples/editor/www/scripts/tsconfig.json
diff --git a/_examples/intermediate/typescript/main.go b/typescript/_examples/typescript/main.go
similarity index 73%
rename from _examples/intermediate/typescript/main.go
rename to typescript/_examples/typescript/main.go
index 29cfa590..a4289974 100644
--- a/_examples/intermediate/typescript/main.go
+++ b/typescript/_examples/typescript/main.go
@@ -2,6 +2,8 @@ package main
 
 import (
 	"github.com/kataras/iris"
+	"github.com/kataras/iris/context"
+
 	"github.com/kataras/iris/typescript"
 )
 
@@ -13,11 +15,17 @@ import (
 func main() {
 	app := iris.New()
 
+	app.StaticWeb("/scripts", "./www") // serve the scripts
+
+	app.Get("/", func(ctx context.Context) {
+		ctx.ServeFile("./www/index.html", false)
+	})
+
 	ts := typescript.New()
 	ts.Config.Dir = "./www/scripts"
-	ts.Attach(app) // attach the application to the typescript compiler
+	ts.Run(app.Logger().Infof)
 
-	app.StaticWeb("/", "./www") // serve the index.html
+	// http://localhost:8080
 	app.Run(iris.Addr(":8080"))
 }
 
@@ -30,4 +38,4 @@ func main() {
 // - compiles the typescript files using default compiler options if not tsconfig found
 // - watches for changes on typescript files, if a change then it recompiles the .ts to .js
 //
-// same as you used to do with gulp-like tools, but here at Iris I do my bests to help GO developers.
+// same as you used to do with gulp-like tools, but here I do my bests to help GO developers.
diff --git a/_examples/intermediate/typescript/www/index.html b/typescript/_examples/typescript/www/index.html
similarity index 69%
rename from _examples/intermediate/typescript/www/index.html
rename to typescript/_examples/typescript/www/index.html
index 92bb58ae..72e7d698 100644
--- a/_examples/intermediate/typescript/www/index.html
+++ b/typescript/_examples/typescript/www/index.html
@@ -1,6 +1,6 @@
 
 
-Load my script (lawl)
+Load my script
 
 
 	
diff --git a/_examples/intermediate/typescript/www/scripts/app.ts b/typescript/_examples/typescript/www/scripts/app.ts
similarity index 83%
rename from _examples/intermediate/typescript/www/scripts/app.ts
rename to typescript/_examples/typescript/www/scripts/app.ts
index 66af02a5..39e1cd77 100644
--- a/_examples/intermediate/typescript/www/scripts/app.ts
+++ b/typescript/_examples/typescript/www/scripts/app.ts
@@ -11,6 +11,6 @@ class User{
 
 }
 
-var user = new User("kataras");
+var user = new User("iris web framework");
 var hi = user.Hi("Hello");
 window.alert(hi);
diff --git a/typescript/config.go b/typescript/config.go
index 405dc939..44efe2a6 100644
--- a/typescript/config.go
+++ b/typescript/config.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package typescript
 
 import (
@@ -169,9 +165,12 @@ func DefaultTsconfig() Tsconfig {
 			Jsx:              "react",
 			ModuleResolution: "classic",
 			Locale:           "en",
-			Watch:            true,
+			Watch:            false,
 			NoImplicitAny:    false,
 			SourceMap:        false,
+			Diagnostics:      true,
+			NoEmit:           false,
+			OutDir:           "", // taken from Config.Dir if it's not empty, otherwise ./ on Run()
 		},
 		Exclude: []string{"node_modules"},
 	}
@@ -183,7 +182,7 @@ func DefaultTsconfig() Tsconfig {
 func DefaultConfig() Config {
 	root, err := os.Getwd()
 	if err != nil {
-		panic("Typescript Adaptor: Cannot get the Current Working Directory !!! [os.getwd()]")
+		panic("typescript: cannot get the cwd")
 	}
 	compilerTsConfig := DefaultTsconfig()
 	c := Config{
diff --git a/typescript/editor/LICENSE b/typescript/editor/LICENSE
deleted file mode 100644
index 788e9ce1..00000000
--- a/typescript/editor/LICENSE
+++ /dev/null
@@ -1,51 +0,0 @@
-Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. 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 Gerasimos Maropoulos nor the name of his
-username, kataras, 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.
-
-Third-Parties:
-
-The MIT License (MIT)
-
-Copyright (c) 2016 Basarat Ali Syed
-
-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.
\ No newline at end of file
diff --git a/typescript/editor/config.go b/typescript/editor/config.go
index 6be81768..c99f1cac 100644
--- a/typescript/editor/config.go
+++ b/typescript/editor/config.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package editor
 
 import (
diff --git a/typescript/editor/editor.go b/typescript/editor/editor.go
index 1634dc5f..020427f7 100644
--- a/typescript/editor/editor.go
+++ b/typescript/editor/editor.go
@@ -1,32 +1,29 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package editor
 
-//  +------------------------------------------------------------+
-//  | Editor usage                                               |
-//  +------------------------------------------------------------+
-//
-// 	import "github.com/kataras/iris/typescript/editor"
-//  [...]
-//
-//  app := iris.New()
-// 	e := editor.New(editor.Config{})
-// 	e.Attach(app)
-//
-//  [...]
-// 	app.Run(iris.Addr(":8080"))
+/* Package editor provides alm-tools cloud editor automation for the iris web framework.
 
-//
-//  +------------------------------------------------------------+
-//  | General notes for authentication                           |
-//  +------------------------------------------------------------+
-//
-// The Authorization specifies the authentication mechanism (in this case Basic) followed by the username and password.
-// Although, the string aHR0cHdhdGNoOmY= may look encrypted it is simply a base64 encoded version of :.
-// Would be readily available to anyone who could intercept the HTTP request.
+Usage:
 
+
+	import "github.com/kataras/iris/typescript/editor"
+	[...]
+
+	app := iris.New()
+	e := editor.New(editor.Config{})
+	e.Run(app.Logger().Infof)
+
+	[...]
+	app.Run(iris.Addr(":8080"))
+	e.Stop()
+
+
+General notes for authentication
+
+
+The Authorization specifies the authentication mechanism (in this case Basic) followed by the username and password.
+Although, the string aHR0cHdhdGNoOmY= may look encrypted it is simply a base64 encoded version of :.
+Would be readily available to anyone who could intercept the HTTP request.
+*/
 import (
 	"bufio"
 	"io"
@@ -34,8 +31,6 @@ import (
 	"path/filepath"
 	"strconv"
 
-	"github.com/kataras/iris"
-	"github.com/kataras/iris/core/host"
 	"github.com/kataras/iris/typescript/npm"
 )
 
@@ -57,6 +52,11 @@ type (
 	}
 )
 
+var (
+	// NoOpLogger can be used as the logger argument, it prints nothing.
+	NoOpLogger = func(string, ...interface{}) {}
+)
+
 // New creates and returns an Editor Plugin instance
 func New(cfg ...Config) *Editor {
 	c := DefaultConfig()
@@ -110,9 +110,9 @@ func (e *Editor) DisableOutput() {
 	e.config.DisableOutput = true
 }
 
-// GetDescription EditorPlugin is a bridge between Iris and the alm-tools, the browser-based IDE for client-side sources.
+// GetDescription EditorPlugin is a bridge between iris and the alm-tools, the browser-based IDE for client-side sources.
 func (e *Editor) GetDescription() string {
-	return "A bridge between Iris and the alm-tools, the browser-based IDE."
+	return "A bridge between iris and the alm-tools, the browser-based IDE."
 }
 
 // we use that editorWriter to prefix the editor's output with "Editor Adaptor: "
@@ -120,9 +120,15 @@ type editorWriter struct {
 	underline io.Writer
 }
 
-// build runs before the server's listens,  creates the listener ( use of port parent hostname:DefaultPort if not exist)
-func (e *Editor) build(s *iris.Application) {
-	e.log = s.Log
+// Run starts the editor's server.
+//
+// Developers should call the `Stop` to shutdown the editor's server when main server will be closed.
+func (e *Editor) Run(logger func(format string, a ...interface{})) {
+	if logger == nil {
+		logger = NoOpLogger
+	}
+
+	e.log = logger
 	if e.config.Hostname == "" {
 		e.config.Hostname = "0.0.0.0"
 	}
@@ -138,8 +144,8 @@ func (e *Editor) build(s *iris.Application) {
 	e.start()
 }
 
-// close kills the editor's server when Iris is closed
-func (e *Editor) close(s *iris.Application) {
+// Stop kills the editor's server.
+func (e *Editor) Stop() {
 	if e.process != nil {
 		err := e.process.Kill()
 		if err != nil {
@@ -210,13 +216,3 @@ func (e *Editor) start() {
 	// no need, alm-tools post these
 	// e.logger.Printf("Editor is running at %s:%d | %s", e.config.Hostname, e.config.Port, e.config.WorkingDir)
 }
-
-// Attach adapts the editor to one or more Iris instance(s).
-func (e *Editor) Attach(app *iris.Application) {
-
-	e.build(app)
-
-	app.Scheduler.Schedule(host.OnInterrupt(func(proc host.TaskProcess) {
-		e.close(app)
-	}))
-}
diff --git a/typescript/npm/exec.go b/typescript/npm/exec.go
index aade8bd8..d26bf889 100644
--- a/typescript/npm/exec.go
+++ b/typescript/npm/exec.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package npm // #nosec
 
 import (
diff --git a/typescript/npm/npm.go b/typescript/npm/npm.go
index 1ebff966..a6e6e71d 100644
--- a/typescript/npm/npm.go
+++ b/typescript/npm/npm.go
@@ -1,18 +1,15 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package npm
 
 import (
 	"fmt"
+	"path/filepath"
 	"strings"
 	"time"
 )
 
 var (
 	// nodeModulesPath is the path of the root npm modules
-	// Ex: C:\\Users\\kataras\\AppData\\Roaming\\npm\\node_modules
+	// Ex: C:\\Users\\iris\\AppData\\Roaming\\npm\\node_modules
 	nodeModulesPath string
 )
 
@@ -118,11 +115,10 @@ func NodeModuleAbs(relativePath string) string {
 // here we have two options
 //1 . search by command something like npm -ls -g --depth=x
 //2.  search on files, we choose the second
-func NodeModuleExists(executableRelativePath string) bool {
-	execAbsPath := NodeModuleAbs(executableRelativePath)
-	if execAbsPath == "" {
-		return false
+func NodeModuleExists(execPath string) bool {
+	if !filepath.IsAbs(execPath) {
+		execPath = NodeModuleAbs(execPath)
 	}
 
-	return Exists(execAbsPath)
+	return Exists(execPath)
 }
diff --git a/typescript/typescript.go b/typescript/typescript.go
index eb110157..6e671e6c 100644
--- a/typescript/typescript.go
+++ b/typescript/typescript.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 // Package typescript provides a typescript compiler with hot-reloader
 // and optionally a cloud-based editor, called 'alm-tools'.
 // typescript (by microsoft) and alm-tools (by @basarat) have their own (open-source) licenses
@@ -15,7 +11,6 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/kataras/iris"
 	"github.com/kataras/iris/typescript/npm"
 )
 
@@ -23,26 +18,55 @@ type (
 	// Typescript contains the unique iris' typescript loader, holds all necessary fields & methods.
 	Typescript struct {
 		Config *Config
-		// taken from framework
-		log func(format string, a ...interface{})
+		log    func(format string, a ...interface{})
 	}
 )
 
 // New creates & returns a new instnace typescript plugin
-func New() *Typescript {
+func New(cfg ...Config) *Typescript {
 	c := DefaultConfig()
-
-	if !strings.Contains(c.Ignore, nodeModules) {
-		c.Ignore += "," + nodeModules
+	if len(cfg) > 0 {
+		c = cfg[0]
 	}
 
 	return &Typescript{Config: &c}
 }
 
-// implementation
+var (
+	// NoOpLogger can be used as the logger argument, it prints nothing.
+	NoOpLogger = func(string, ...interface{}) {}
+)
+
+// Run starts the typescript filewatcher watcher and the typescript compiler.
+func (t *Typescript) Run(logger func(format string, a ...interface{})) {
+	c := t.Config
+	if c.Tsconfig == nil {
+		tsC := DefaultTsconfig()
+		c.Tsconfig = &tsC
+	}
+
+	if c.Dir == "" {
+		c.Tsconfig.CompilerOptions.OutDir = c.Dir
+	}
+
+	if c.Dir == "" {
+		c.Dir = "./"
+	}
+
+	if !strings.Contains(c.Ignore, nodeModules) {
+		c.Ignore += "," + nodeModules
+	}
+
+	if logger == nil {
+		logger = NoOpLogger
+	}
+
+	t.log = logger
+
+	t.start()
+}
 
 func (t *Typescript) start() {
-
 	if t.hasTypescriptFiles() {
 		//Can't check if permission denied returns always exists = true....
 
@@ -59,7 +83,7 @@ func (t *Typescript) start() {
 		projects := t.getTypescriptProjects()
 		if len(projects) > 0 {
 			watchedProjects := 0
-			//typescript project (.tsconfig) found
+			// typescript project (.tsconfig) found
 			for _, project := range projects {
 				cmd := npm.CommandBuilder("node", t.Config.Bin, "-p", project[0:strings.LastIndex(project, npm.PathSeparator)]) //remove the /tsconfig.json)
 				projectConfig, perr := FromFile(project)
@@ -74,70 +98,76 @@ func (t *Typescript) start() {
 					go func() {
 						_, err := cmd.Output()
 						if err != nil {
-							t.log(err.Error())
+							t.log("error when 'watch' is true: %v", err)
 							return
 						}
 					}()
 				} else {
-
 					_, err := cmd.Output()
 					if err != nil {
-						t.log(err.Error())
+						t.log("unexpected error from output: %v", err)
 						return
 					}
 
 				}
 
 			}
-			// t.log("%d Typescript project(s) compiled ( %d monitored by a background file watcher", len(projects), watchedProjects)
-		} else {
-			//search for standalone typescript (.ts) files and compile them
-			files := t.getTypescriptFiles()
-			if len(files) > 0 {
-				/* watchedFiles := 0
-				if t.Config.Tsconfig.CompilerOptions.Watch {
-					watchedFiles = len(files)
-				}*/
-				//it must be always > 0 if we came here, because of if hasTypescriptFiles == true.
-				for _, file := range files {
-					absPath, err := filepath.Abs(file)
-					if err != nil {
-						continue
-					}
-
-					//these will be used if no .tsconfig found.
-					// cmd := npm.CommandBuilder("node", t.Config.Bin)
-					// cmd.Arguments(t.Config.Bin, t.Config.Tsconfig.CompilerArgs()...)
-					// cmd.AppendArguments(absPath)
-					compilerArgs := t.Config.Tsconfig.CompilerArgs()
-					cmd := npm.CommandBuilder("node", t.Config.Bin)
-					for _, s := range compilerArgs {
-						cmd.AppendArguments(s)
-					}
-					cmd.AppendArguments(absPath)
-					go func() {
-						compilerMsgB, _ := cmd.Output()
-						compilerMsg := string(compilerMsgB)
-						cmd.Args = cmd.Args[0 : len(cmd.Args)-1] //remove the last, which is the file
-
-						if strings.Contains(compilerMsg, "error") {
-							t.log(compilerMsg)
-						}
-
-					}()
-
-				}
-				// t.log("%d Typescript file(s) compiled ( %d monitored by a background file watcher )", len(files), watchedFiles)
-			}
-
+			return
 		}
+		//search for standalone typescript (.ts) files and compile them
+		files := t.getTypescriptFiles()
+		if len(files) > 0 {
+			/* watchedFiles := 0
+			if t.Config.Tsconfig.CompilerOptions.Watch {
+				watchedFiles = len(files)
+			}*/
+			//it must be always > 0 if we came here, because of if hasTypescriptFiles == true.
+			for _, file := range files {
 
+				absPath, err := filepath.Abs(file)
+				if err != nil {
+					t.log("error while trying to resolve absolute path for %s: %v", file, err)
+					continue
+				}
+
+				// these will be used if no .tsconfig found.
+				// cmd := npm.CommandBuilder("node", t.Config.Bin)
+				// cmd.Arguments(t.Config.Bin, t.Config.Tsconfig.CompilerArgs()...)
+				// cmd.AppendArguments(absPath)
+				compilerArgs := t.Config.Tsconfig.CompilerArgs()
+				cmd := npm.CommandBuilder("node", t.Config.Bin)
+
+				for _, s := range compilerArgs {
+					cmd.AppendArguments(s)
+				}
+				cmd.AppendArguments(absPath)
+				go func() {
+					compilerMsgB, _ := cmd.Output()
+					compilerMsg := string(compilerMsgB)
+					cmd.Args = cmd.Args[0 : len(cmd.Args)-1] //remove the last, which is the file
+
+					if strings.Contains(compilerMsg, "error") {
+						t.log(compilerMsg)
+					}
+
+				}()
+
+			}
+			return
+		}
+		return
 	}
+	absPath, err := filepath.Abs(t.Config.Dir)
+	if err != nil {
+		t.log("no typescript file, the directory cannot be resolved: %v", err)
+		return
+	}
+	t.log("no typescript files found on : %s", absPath)
 }
 
 func (t *Typescript) hasTypescriptFiles() bool {
 	root := t.Config.Dir
-	ignoreFolders := strings.Split(t.Config.Ignore, ",")
+	ignoreFolders := t.getIgnoreFolders()
 	hasTs := false
 	if !npm.Exists(root) {
 		t.log("typescript error: directory '%s' couldn't be found,\nplease specify a valid path for your *.ts files", root)
@@ -145,18 +175,18 @@ func (t *Typescript) hasTypescriptFiles() bool {
 	}
 	// ignore error
 	filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
-
 		if fi.IsDir() {
 			return nil
 		}
-		for i := range ignoreFolders {
-			if strings.Contains(path, ignoreFolders[i]) {
-				return nil
+		for _, s := range ignoreFolders {
+			if strings.HasSuffix(path, s) || path == s {
+				return filepath.SkipDir
 			}
 		}
+
 		if strings.HasSuffix(path, ".ts") {
 			hasTs = true
-			return errors.New("Typescript found, hope that will stop here")
+			return errors.New("typescript found, hope that will stop here")
 		}
 
 		return nil
@@ -164,9 +194,21 @@ func (t *Typescript) hasTypescriptFiles() bool {
 	return hasTs
 }
 
+func (t *Typescript) getIgnoreFolders() (folders []string) {
+	ignoreFolders := strings.Split(t.Config.Ignore, ",")
+
+	for _, s := range ignoreFolders {
+		if s != "" {
+			folders = append(folders, s)
+		}
+	}
+
+	return folders
+}
+
 func (t *Typescript) getTypescriptProjects() []string {
 	var projects []string
-	ignoreFolders := strings.Split(t.Config.Ignore, ",")
+	ignoreFolders := t.getIgnoreFolders()
 
 	root := t.Config.Dir
 	//t.logger.Printf("\nSearching for typescript projects in %s", root)
@@ -176,9 +218,8 @@ func (t *Typescript) getTypescriptProjects() []string {
 		if fi.IsDir() {
 			return nil
 		}
-		for i := range ignoreFolders {
-			if strings.Contains(path, ignoreFolders[i]) {
-				//t.logger.Println(path + " ignored")
+		for _, s := range ignoreFolders {
+			if strings.HasSuffix(path, s) || path == s {
 				return filepath.SkipDir
 			}
 		}
@@ -196,7 +237,7 @@ func (t *Typescript) getTypescriptProjects() []string {
 // this is being called if getTypescriptProjects return 0 len, then we are searching for files using that:
 func (t *Typescript) getTypescriptFiles() []string {
 	var files []string
-	ignoreFolders := strings.Split(t.Config.Ignore, ",")
+	ignoreFolders := t.getIgnoreFolders()
 
 	root := t.Config.Dir
 
@@ -205,9 +246,8 @@ func (t *Typescript) getTypescriptFiles() []string {
 		if fi.IsDir() {
 			return nil
 		}
-		for i := range ignoreFolders {
-			if strings.Contains(path, ignoreFolders[i]) {
-				//t.logger.Println(path + " ignored")
+		for _, s := range ignoreFolders {
+			if strings.HasSuffix(path, s) || path == s {
 				return nil
 			}
 		}
@@ -221,9 +261,3 @@ func (t *Typescript) getTypescriptFiles() []string {
 	})
 	return files
 }
-
-// Attach attaches the typescript to one or more Iris instance(s).
-func (t *Typescript) Attach(app *iris.Application) {
-	t.log = app.Log
-	t.start()
-}
diff --git a/view/LICENSE b/view/LICENSE
deleted file mode 100644
index 6d6f9946..00000000
--- a/view/LICENSE
+++ /dev/null
@@ -1,111 +0,0 @@
-Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. 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 Gerasimos Maropoulos nor the name of his
-username, kataras, 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.
-
-
-Third-Parties:
-
-The MIT License (MIT)
-
-Copyright (c) 2013-2014 Florian Schlachter
-
-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.
-
-Copyright (c) 2015, Joker
-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 jade 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 HOLDER 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.
-
-(The MIT License)
-
-Copyright (c) 2012 Ekin Koc ekin@eknkc.com
-
-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.
-
-The MIT License (MIT)
-
-Copyright (c) 2015 Aymerick JEHANNE
-
-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.
diff --git a/view/amber.go b/view/amber.go
index 29cba814..32547830 100644
--- a/view/amber.go
+++ b/view/amber.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package view
 
 import (
diff --git a/view/django.go b/view/django.go
index 087e3dff..93cfca45 100644
--- a/view/django.go
+++ b/view/django.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package view
 
 import (
diff --git a/view/engine.go b/view/engine.go
index 533c5e30..93c99e70 100644
--- a/view/engine.go
+++ b/view/engine.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package view
 
 import (
@@ -24,16 +20,7 @@ func getLayout(layout string, globalLayout string) string {
 	return layout
 }
 
-// Options should contains the dynamic options for the engine's ExecuteWriter.
-type Options interface {
-	// the per-execute layout,
-	// most view engines will have a static configuration field for that too.
-	GetLayout() string
-	// should returns the dynamic binding data, which will be used inside the template file
-	GetData() interface{}
-} // this Options interface is implemented inside context, in order to use one import path for all context's methods.
-
-// Engine is the interface which all viwe engines should be implemented in order to be adapted inside Iris.
+// Engine is the interface which all view engines should be implemented in order to be registered inside iris.
 type Engine interface {
 	// Load should load the templates from a directory of by binary(assets/go-bindata).
 	Load() error
diff --git a/view/funcs.go b/view/funcs.go
index e38d3059..7fd34f98 100644
--- a/view/funcs.go
+++ b/view/funcs.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package view
 
 // EngineFuncer is an addition of a view engine,
diff --git a/view/handlebars.go b/view/handlebars.go
index f0310bc8..fac1d85e 100644
--- a/view/handlebars.go
+++ b/view/handlebars.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package view
 
 import (
diff --git a/view/html.go b/view/html.go
index ba19b820..eaee8597 100644
--- a/view/html.go
+++ b/view/html.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package view
 
 import (
@@ -122,7 +118,7 @@ func (s *HTMLEngine) Delims(left, right string) *HTMLEngine {
 //         // mainLayout.html is inside: "./templates/layouts/".
 //
 // Note: Layout can be changed for a specific call
-// action with the option: "layout" on the Iris' context.Render function.
+// action with the option: "layout" on the iris' context.Render function.
 func (s *HTMLEngine) Layout(layoutFile string) *HTMLEngine {
 	s.layout = layoutFile
 	return s
@@ -183,9 +179,7 @@ func (s *HTMLEngine) loadDirectory() error {
 
 	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
 		if info == nil || info.IsDir() {
-
 		} else {
-
 			rel, err := filepath.Rel(dir, path)
 			if err != nil {
 				templateErr = err
@@ -203,26 +197,23 @@ func (s *HTMLEngine) loadDirectory() error {
 
 				contents := string(buf)
 
-				if err == nil {
+				name := filepath.ToSlash(rel)
+				tmpl := s.Templates.New(name)
 
-					name := filepath.ToSlash(rel)
-					tmpl := s.Templates.New(name)
-
-					if s.middleware != nil {
-						contents, err = s.middleware(name, contents)
-					}
-					if err != nil {
-						templateErr = err
-						return err
-					}
-					s.mu.Lock()
-					// Add our funcmaps.
-					if s.funcs != nil {
-						tmpl.Funcs(s.funcs)
-					}
-
-					tmpl.Funcs(emptyFuncs).Parse(contents)
-					s.mu.Unlock()
+				if s.middleware != nil {
+					contents, err = s.middleware(name, contents)
+				}
+				if err != nil {
+					templateErr = err
+					return err
+				}
+				s.mu.Lock()
+				// Add our funcmaps.
+				_, err = tmpl.Funcs(emptyFuncs).Funcs(s.funcs).Parse(contents)
+				s.mu.Unlock()
+				if err != nil {
+					templateErr = err
+					return err
 				}
 			}
 
@@ -282,11 +273,7 @@ func (s *HTMLEngine) loadAssets() error {
 			}
 
 			// Add our funcmaps.
-			if s.funcs != nil {
-				tmpl.Funcs(s.funcs)
-			}
-
-			tmpl.Funcs(emptyFuncs).Parse(contents)
+			tmpl.Funcs(emptyFuncs).Funcs(s.funcs).Parse(contents)
 		}
 	}
 	return templateErr
diff --git a/view/pug.go b/view/pug.go
index cef595af..ed72432a 100644
--- a/view/pug.go
+++ b/view/pug.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package view
 
 import (
diff --git a/view/view.go b/view/view.go
index 1caf406b..c551fa0a 100644
--- a/view/view.go
+++ b/view/view.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package view
 
 import (
@@ -18,10 +14,9 @@ type View struct {
 	engines []Engine
 }
 
-// Register loads all the view engines' template files or embedded assets.
-func (v *View) Register(e Engine) error {
+// Register registers a view engine.
+func (v *View) Register(e Engine) {
 	v.engines = append(v.engines, e)
-	return nil
 }
 
 // Find receives a filename, gets its extension and returns the view engine responsible for that file extension
diff --git a/websocket/AUTHORS b/websocket/AUTHORS
new file mode 100644
index 00000000..3238ad98
--- /dev/null
+++ b/websocket/AUTHORS
@@ -0,0 +1,5 @@
+# This is the official list of Iris Websocket authors for copyright
+# purposes.
+
+Gerasimos Maropoulos 
+Bill Qeras, Jr. 
\ No newline at end of file
diff --git a/websocket/LICENSE b/websocket/LICENSE
index 85b81854..46db5573 100644
--- a/websocket/LICENSE
+++ b/websocket/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
+Copyright (c) 2017 The Iris Websocket Authors. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
@@ -10,8 +10,8 @@ notice, this list of conditions and the following disclaimer.
 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 Gerasimos Maropoulos nor the name of his
-username, kataras, may be used to endorse or promote products derived from
+   * 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
@@ -24,29 +24,4 @@ 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.
-
-Third-Parties:
-
-Copyright (c) 2013 The Gorilla WebSocket 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.
-
-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 HOLDER 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.
\ No newline at end of file
diff --git a/websocket/README.md b/websocket/README.md
new file mode 100644
index 00000000..1dd2af53
--- /dev/null
+++ b/websocket/README.md
@@ -0,0 +1,11 @@
+# Websocket
+
+Rich websocket support for the [iris](https://github.com/kataras/iris) web framework.
+
+## Table of contents
+
+* [Chat](_examples/chat/main.go)
+* [Native Messages](_examples/native-messages/main.go)
+* [Connection List](_examples/connectionlist/main.go)
+* [TLS Enabled](_examples/secure/main.go)
+* [Custom Raw Go Client](_examples/custom-go-client/main.go)
\ No newline at end of file
diff --git a/_examples/intermediate/websockets/ridiculous-simple/main.go b/websocket/_examples/chat/main.go
similarity index 63%
rename from _examples/intermediate/websockets/ridiculous-simple/main.go
rename to websocket/_examples/chat/main.go
index ac8865d4..0ef81f8a 100644
--- a/_examples/intermediate/websockets/ridiculous-simple/main.go
+++ b/websocket/_examples/chat/main.go
@@ -9,44 +9,48 @@ import (
 	"github.com/kataras/iris/websocket"
 )
 
-func handleConnection(c websocket.Connection) {
-
-	// Read events from browser
-	c.On("chat", func(msg string) {
-
-		// Print the message to the console
-		fmt.Printf("%s sent: %s\n", c.Context().RemoteAddr(), msg)
-
-		// Write message back to the client message owner:
-		// c.Emit("chat", msg)
-
-		c.To(websocket.Broadcast).Emit("chat", msg)
-	})
-
-}
-
 func main() {
 	app := iris.New()
 
-	// create our echo websocket server
-	ws := websocket.New(websocket.Config{
-		ReadBufferSize:  1024,
-		WriteBufferSize: 1024,
-		Endpoint:        "/echo",
-	})
-
-	ws.OnConnection(handleConnection)
-
-	// Adapt the application to the websocket server.
-	ws.Attach(app)
-
 	app.Get("/", func(ctx context.Context) {
 		ctx.ServeFile("websockets.html", false) // second parameter: enable gzip?
 	})
 
+	setupWebsocket(app)
+
 	// x2
 	// http://localhost:8080
 	// http://localhost:8080
 	// write something, press submit, see the result.
 	app.Run(iris.Addr(":8080"))
 }
+
+func setupWebsocket(app *iris.Application) {
+	// create our echo websocket server
+	ws := websocket.New(websocket.Config{
+		ReadBufferSize:  1024,
+		WriteBufferSize: 1024,
+	})
+	ws.OnConnection(handleConnection)
+
+	// register the server on an endpoint.
+	// see the inline javascript code in the websockets.html, this endpoint is used to connect to the server.
+	app.Get("/echo", ws.Handler())
+
+	// serve the javascript built'n client-side library,
+	// see weboskcets.html script tags, this path is used.
+	app.Any("/iris-ws.js", func(ctx context.Context) {
+		ctx.Write(websocket.ClientSource)
+	})
+}
+
+func handleConnection(c websocket.Connection) {
+	// Read events from browser
+	c.On("chat", func(msg string) {
+		// Print the message to the console, c.Context() is the iris's http context.
+		fmt.Printf("%s sent: %s\n", c.Context().RemoteAddr(), msg)
+		// Write message back to the client message owner:
+		// c.Emit("chat", msg)
+		c.To(websocket.Broadcast).Emit("chat", msg)
+	})
+}
diff --git a/_examples/intermediate/websockets/ridiculous-simple/websockets.html b/websocket/_examples/chat/websockets.html
similarity index 100%
rename from _examples/intermediate/websockets/ridiculous-simple/websockets.html
rename to websocket/_examples/chat/websockets.html
diff --git a/_examples/intermediate/websockets/connectionlist/main.go b/websocket/_examples/connectionlist/main.go
similarity index 58%
rename from _examples/intermediate/websockets/connectionlist/main.go
rename to websocket/_examples/connectionlist/main.go
index 91560ade..97097fe9 100644
--- a/_examples/intermediate/websockets/connectionlist/main.go
+++ b/websocket/_examples/connectionlist/main.go
@@ -7,8 +7,8 @@ import (
 
 	"github.com/kataras/iris"
 	"github.com/kataras/iris/context"
-
 	"github.com/kataras/iris/view"
+
 	"github.com/kataras/iris/websocket"
 )
 
@@ -19,36 +19,20 @@ type clientPage struct {
 
 func main() {
 	app := iris.New()
-	app.AttachView(view.HTML("./templates", ".html")) // select the html engine to serve templates
+	app.RegisterView(view.HTML("./templates", ".html")) // select the html engine to serve templates
 
-	ws := websocket.New(websocket.Config{
-		// the request path which the websocket client should listen/registered to the server,
-		// the endpoint is like a route.
-		Endpoint: "/my_endpoint",
-		// the client-side javascript static file path
-		// which will be served by Iris.
-		// default is /iris-ws.js
-		// if you change that you have to change the bottom of templates/client.html
-		// script tag:
-		ClientSourcePath: "/iris-ws.js",
-		//
-		// Set the timeouts, 0 means no timeout
-		// websocket has more configuration, go to ../../config.go for more:
-		// WriteTimeout: 0,
-		// ReadTimeout:  0,
-		// by-default all origins are accepted, you can change this behavior by setting:
-		// CheckOrigin: (r *http.Request ) bool {},
-		//
-		//
-		// IDGenerator used to create (and later on, set)
-		// an ID for each incoming websocket connections (clients).
-		// The request is an argument which you can use to generate the ID (from headers for example).
-		// If empty then the ID is generated by DefaultIDGenerator: randomString(64):
-		// IDGenerator func(ctx context.Context) string {},
+	ws := websocket.New(websocket.Config{})
+
+	// 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", ws.Handler())
+
+	// serve the javascript built'n client-side library,
+	// see weboskcets.html script tags, this path is used.
+	app.Any("/iris-ws.js", func(ctx context.Context) {
+		ctx.Write(websocket.ClientSource)
 	})
 
-	ws.Attach(app) // adapt the application to the websocket server
-
 	app.StaticWeb("/js", "./static/js") // serve our custom javascript code
 
 	app.Get("/", func(ctx context.Context) {
diff --git a/_examples/intermediate/websockets/connectionlist/static/js/chat.js b/websocket/_examples/connectionlist/static/js/chat.js
similarity index 100%
rename from _examples/intermediate/websockets/connectionlist/static/js/chat.js
rename to websocket/_examples/connectionlist/static/js/chat.js
diff --git a/_examples/intermediate/websockets/connectionlist/templates/client.html b/websocket/_examples/connectionlist/templates/client.html
similarity index 89%
rename from _examples/intermediate/websockets/connectionlist/templates/client.html
rename to websocket/_examples/connectionlist/templates/client.html
index 04ad2115..35db0daa 100644
--- a/_examples/intermediate/websockets/connectionlist/templates/client.html
+++ b/websocket/_examples/connectionlist/templates/client.html
@@ -15,7 +15,7 @@
 		var HOST = {{.Host}}
 	
 	
-	
+	
 	
 	
 	
diff --git a/_examples/intermediate/websockets/custom-go-client/main.go b/websocket/_examples/custom-go-client/main.go
similarity index 95%
rename from _examples/intermediate/websockets/custom-go-client/main.go
rename to websocket/_examples/custom-go-client/main.go
index 84625247..d2965aff 100644
--- a/_examples/intermediate/websockets/custom-go-client/main.go
+++ b/websocket/_examples/custom-go-client/main.go
@@ -9,10 +9,10 @@ import (
 	"strings"
 	"time"
 
-	xwebsocket "golang.org/x/net/websocket"
-
 	"github.com/kataras/iris"
 	"github.com/kataras/iris/websocket"
+
+	xwebsocket "golang.org/x/net/websocket"
 )
 
 // WS is the current websocket connection
@@ -135,12 +135,14 @@ func OnConnect(c websocket.Connection) {
 func ServerLoop() {
 	app := iris.New()
 
-	ws := websocket.New(websocket.Config{Endpoint: "/socket"})
-	ws.Attach(app)
+	ws := websocket.New(websocket.Config{})
+
+	// 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("/socket", ws.Handler())
 
 	ws.OnConnection(OnConnect)
 	app.Run(iris.Addr(":8080"))
-
 }
 
 // OnJoin handles Join broadcast group request
diff --git a/_examples/intermediate/websockets/native-messages/main.go b/websocket/_examples/native-messages/main.go
similarity index 74%
rename from _examples/intermediate/websockets/native-messages/main.go
rename to websocket/_examples/native-messages/main.go
index 4110c116..48b804e7 100644
--- a/_examples/intermediate/websockets/native-messages/main.go
+++ b/websocket/_examples/native-messages/main.go
@@ -5,8 +5,8 @@ import (
 
 	"github.com/kataras/iris"
 	"github.com/kataras/iris/context"
-
 	"github.com/kataras/iris/view"
+
 	"github.com/kataras/iris/websocket"
 )
 
@@ -15,7 +15,7 @@ Use of: OnMessage and EmitMessage.
 
 
 NOTICE: IF YOU HAVE RAN THE PREVIOUS EXAMPLES YOU HAVE TO CLEAR YOUR BROWSER's CACHE
-BECAUSE chat.js is different than the CACHED. OTHERWISE YOU WILL GET Ws is undefined from the browser's console, becuase it will use the cached.
+BECAUSE chat.js is different than the CACHED. OTHERWISE YOU WILL GET Ws is undefined from the browser's console, because it will use the cached.
 */
 
 type clientPage struct {
@@ -26,16 +26,16 @@ type clientPage struct {
 func main() {
 	app := iris.New()
 
-	app.AttachView(view.HTML("./templates", ".html")) // select the html engine to serve templates
+	app.RegisterView(view.HTML("./templates", ".html")) // select the html engine to serve templates
 
 	ws := websocket.New(websocket.Config{
-		// the path which the websocket client should listen/registered to,
-		Endpoint: "/my_endpoint",
-		// to enable binary messages (useful for protobuf):
-		// BinaryMessages: true,
+	// to enable binary messages (useful for protobuf):
+	// BinaryMessages: true,
 	})
 
-	ws.Attach(app) // adapt the websocket server, you can adapt more than one with different Endpoint
+	// 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", ws.Handler())
 
 	app.StaticWeb("/js", "./static/js") // serve our custom javascript code
 
diff --git a/_examples/intermediate/websockets/native-messages/static/js/chat.js b/websocket/_examples/native-messages/static/js/chat.js
similarity index 100%
rename from _examples/intermediate/websockets/native-messages/static/js/chat.js
rename to websocket/_examples/native-messages/static/js/chat.js
diff --git a/_examples/intermediate/websockets/native-messages/templates/client.html b/websocket/_examples/native-messages/templates/client.html
similarity index 100%
rename from _examples/intermediate/websockets/native-messages/templates/client.html
rename to websocket/_examples/native-messages/templates/client.html
diff --git a/_examples/intermediate/websockets/secure/main.go b/websocket/_examples/secure/main.go
similarity index 90%
rename from _examples/intermediate/websockets/secure/main.go
rename to websocket/_examples/secure/main.go
index 728ad5de..951f8081 100644
--- a/_examples/intermediate/websockets/secure/main.go
+++ b/websocket/_examples/secure/main.go
@@ -1,15 +1,15 @@
 package main
 
 import (
-	"fmt"       // optional
-	"io/ioutil" // optional
-	"os"        // optional
-	"time"      // optional
+	"fmt"
+	"io/ioutil"
+	"os"
+	"time"
 
 	"github.com/kataras/iris"
 	"github.com/kataras/iris/context"
-
 	"github.com/kataras/iris/view"
+
 	"github.com/kataras/iris/websocket"
 )
 
@@ -20,22 +20,27 @@ type clientPage struct {
 
 func main() {
 	app := iris.New()
-	app.AttachView(view.HTML("./templates", ".html")) // select the html engine to serve templates
+	app.RegisterView(view.HTML("./templates", ".html")) // select the html engine to serve templates
 
-	ws := websocket.New(websocket.Config{
-		Endpoint:         "/my_endpoint",
-		ClientSourcePath: "/iris-ws.js",
+	ws := websocket.New(websocket.Config{})
+
+	// 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", ws.Handler())
+
+	// serve the javascript built'n client-side library,
+	// see weboskcets.html script tags, this path is used.
+	app.Any("/iris-ws.js", func(ctx context.Context) {
+		ctx.Write(websocket.ClientSource)
 	})
 
-	ws.Attach(app)
-
 	app.StaticWeb("/js", "./static/js")
 	app.Get("/", func(ctx context.Context) {
 		// send our custom javascript source file before client really asks for that
 		// using the go v1.8's HTTP/2 Push.
 		// Note that you have to listen using ListenTLS in order this to work.
 		if err := ctx.ResponseWriter().Push("/js/chat.js", nil); err != nil {
-			ctx.Application().Log(err.Error())
+			ctx.Application().Logger().Warnln(err.Error())
 		}
 		ctx.ViewData("", clientPage{"Client Page", ctx.Host()})
 		ctx.View("client.html")
diff --git a/_examples/intermediate/websockets/secure/static/js/chat.js b/websocket/_examples/secure/static/js/chat.js
similarity index 100%
rename from _examples/intermediate/websockets/secure/static/js/chat.js
rename to websocket/_examples/secure/static/js/chat.js
diff --git a/_examples/intermediate/websockets/secure/templates/client.html b/websocket/_examples/secure/templates/client.html
similarity index 88%
rename from _examples/intermediate/websockets/secure/templates/client.html
rename to websocket/_examples/secure/templates/client.html
index 03387567..437b8904 100644
--- a/_examples/intermediate/websockets/secure/templates/client.html
+++ b/websocket/_examples/secure/templates/client.html
@@ -15,7 +15,7 @@
 		var HOST = {{.Host}}
 	
 	
-	
+	
 	
 	
 	
diff --git a/websocket/client.go b/websocket/client.go
index 5788893f..77d06962 100644
--- a/websocket/client.go
+++ b/websocket/client.go
@@ -1,20 +1,29 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package websocket
 
-// ------------------------------------------------------------------------------------
-// ------------------------------------------------------------------------------------
-// ----------------Client side websocket javascript source which is typescript compiled
-// ------------------------------------------------------------------------------------
-// ------------------------------------------------------------------------------------
+import (
+	"time"
+
+	"github.com/kataras/iris/context"
+)
+
+// ClientHandler is the handler which serves the javascript client-side
+// library. It uses a small cache based on the iris/context.StaticCacheDuration.
+func ClientHandler() context.Handler {
+	modNow := time.Now()
+	return func(ctx context.Context) {
+		ctx.ContentType("application/javascript")
+		if _, err := ctx.WriteWithExpiration(ClientSource, modNow); err != nil {
+			ctx.StatusCode(500)
+			ctx.StopExecution()
+			// ctx.Application().Logger().Infof("error while serving []byte via StaticContent: %s", err.Error())
+		}
+	}
+}
 
 // ClientSource the client-side javascript raw source code
 var ClientSource = []byte(`var websocketStringMessageType = 0;
 var websocketIntMessageType = 1;
 var websocketBoolMessageType = 2;
-// bytes is missing here for reasons I will explain somewhen
 var websocketJSONMessageType = 4;
 var websocketMessagePrefix = "iris-websocket-message:";
 var websocketMessageSeparator = ";";
@@ -102,7 +111,7 @@ var Ws = (function () {
         return this._msg(event, t, m);
     };
     Ws.prototype.decodeMessage = function (event, websocketMessage) {
-        //q-websocket-message;user;4;themarshaledstringfromajsonstruct
+        //iris-websocket-message;user;4;themarshaledstringfromajsonstruct
         var skipLen = websocketMessagePrefixLen + websocketMessageSeparatorLen + event.length + 2;
         if (websocketMessage.length < skipLen + 1) {
             return null;
@@ -145,7 +154,7 @@ var Ws = (function () {
     // if native message then calls the fireNativeMessage
     // else calls the fireMessage
     //
-    // remember q gives you the freedom of native websocket messages if you don't want to use this client side at all.
+    // remember iris gives you the freedom of native websocket messages if you don't want to use this client side at all.
     Ws.prototype.messageReceivedFromConn = function (evt) {
         //check if qws message
         var message = evt.data;
@@ -213,7 +222,7 @@ var Ws = (function () {
     Ws.prototype.EmitMessage = function (websocketMessage) {
         this.conn.send(websocketMessage);
     };
-    // Emit sends an q-custom websocket message
+    // Emit sends an iris-custom websocket message
     Ws.prototype.Emit = function (event, data) {
         var messageStr = this.encodeMessage(event, data);
         this.EmitMessage(messageStr);
diff --git a/websocket/client.ts b/websocket/client.ts
index 8644f664..c7ed33d6 100644
--- a/websocket/client.ts
+++ b/websocket/client.ts
@@ -1,14 +1,4 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// -------------------------------------------------------------------------------------
-// -------------------------------------------------------------------------------------
-// ----------------Client side websocket commented typescript source code --------------
-// -------------------------------------------------------------------------------------
-// -------------------------------------------------------------------------------------
-
-// export to client.go:clientSource []byte
+// export to client.go:ClientSource []byte
 
 const websocketStringMessageType = 0;
 const websocketIntMessageType = 1;
@@ -124,7 +114,7 @@ class Ws {
     }
 
     private decodeMessage(event: string, websocketMessage: string): T | any {
-        //q-websocket-message;user;4;themarshaledstringfromajsonstruct
+        //iris-websocket-message;user;4;themarshaledstringfromajsonstruct
         let skipLen = websocketMessagePrefixLen + websocketMessageSeparatorLen + event.length + 2;
         if (websocketMessage.length < skipLen + 1) {
             return null;
@@ -169,7 +159,7 @@ class Ws {
     // if native message then calls the fireNativeMessage
     // else calls the fireMessage
     //
-    // remember q gives you the freedom of native websocket messages if you don't want to use this client side at all.
+    // remember iris gives you the freedom of native websocket messages if you don't want to use this client side at all.
     private messageReceivedFromConn(evt: MessageEvent): void {
         //check if qws message
         let message = evt.data;
@@ -252,7 +242,7 @@ class Ws {
         this.conn.send(websocketMessage);
     }
 
-    // Emit sends an q-custom websocket message
+    // Emit sends an iris-custom websocket message
     Emit(event: string, data: any): void {
         let messageStr = this.encodeMessage(event, data);
         this.EmitMessage(messageStr);
diff --git a/websocket/config.go b/websocket/config.go
index f36695ac..a356eb50 100644
--- a/websocket/config.go
+++ b/websocket/config.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package websocket
 
 import (
@@ -40,15 +36,16 @@ var (
 // Config the websocket server configuration
 // all of these are optional.
 type Config struct {
-	// Endpoint is the path which the websocket server will listen for clients/connections
-	// Default value is empty string, if you don't set it the Websocket server is disabled.
-	Endpoint string
-	// ClientSourcePath is is the path which the client-side
-	// will be auto-served when the server adapted to an Iris station.
-	// Default value is "/iris-ws.js"
-	ClientSourcePath string
-	Error            func(w http.ResponseWriter, r *http.Request, status int, reason error)
-	CheckOrigin      func(r *http.Request) bool
+	// IDGenerator used to create (and later on, set)
+	// an ID for each incoming websocket connections (clients).
+	// The request is an argument which you can use to generate the ID (from headers for example).
+	// If empty then the ID is generated by DefaultIDGenerator: randomString(64)
+	IDGenerator func(ctx context.Context) string
+
+	Error       func(w http.ResponseWriter, r *http.Request, status int, reason error)
+	CheckOrigin func(r *http.Request) bool
+	// HandshakeTimeout specifies the duration for the handshake to complete.
+	HandshakeTimeout time.Duration
 	// WriteTimeout time allowed to write a message to the connection.
 	// 0 means no timeout.
 	// Default value is 0
@@ -76,11 +73,11 @@ type Config struct {
 	// WriteBufferSize is the buffer size for the underline writer
 	// Default value is 4096
 	WriteBufferSize int
-	// IDGenerator used to create (and later on, set)
-	// an ID for each incoming websocket connections (clients).
-	// The request is an argument which you can use to generate the ID (from headers for example).
-	// If empty then the ID is generated by DefaultIDGenerator: randomString(64)
-	IDGenerator func(ctx context.Context) string
+	// EnableCompression specify if the server should attempt to negotiate per
+	// message compression (RFC 7692). Setting this value to true does not
+	// guarantee that compression will be supported. Currently only "no context
+	// takeover" modes are supported.
+	EnableCompression bool
 
 	// Subprotocols specifies the server's supported protocols in order of
 	// preference. If this field is set, then the Upgrade method negotiates a
@@ -91,11 +88,6 @@ type Config struct {
 
 // Validate validates the configuration
 func (c Config) Validate() Config {
-
-	if c.ClientSourcePath == "" {
-		c.ClientSourcePath = DefaultClientSourcePath
-	}
-
 	// 0 means no timeout.
 	if c.WriteTimeout < 0 {
 		c.WriteTimeout = DefaultWebsocketWriteTimeout
diff --git a/websocket/connection.go b/websocket/connection.go
index f2cff78a..ef18b967 100644
--- a/websocket/connection.go
+++ b/websocket/connection.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package websocket
 
 import (
@@ -142,6 +138,12 @@ type (
 		// ID returns the connection's identifier
 		ID() string
 
+		// Server returns the websocket server instance
+		// which this connection is listening to.
+		//
+		// Its connection-relative operations are safe for use.
+		Server() *Server
+
 		// Context returns the (upgraded) context.Context of this connection
 		// avoid using it, you normally don't need it,
 		// websocket has everything you need to authenticate the user BUT if it's necessary
@@ -150,12 +152,12 @@ type (
 
 		// OnDisconnect registers a callback which fires when this connection is closed by an error or manual
 		OnDisconnect(DisconnectFunc)
-		// OnStatusCode registers a callback which fires when this connection occurs an error
-		OnStatusCode(ErrorFunc)
+		// OnError registers a callback which fires when this connection occurs an error
+		OnError(ErrorFunc)
 		// FireStatusCode can be used to send a custom error message to the connection
 		//
-		// It does nothing more than firing the OnStatusCode listeners. It doesn't sends anything to the client.
-		FireStatusCode(errorMessage string)
+		// It does nothing more than firing the OnError listeners. It doesn't sends anything to the client.
+		FireOnError(errorMessage string)
 		// To defines where server should send a message
 		// returns an emitter to send messages
 		To(string) Emitter
@@ -209,7 +211,7 @@ type (
 		// access to the Context, use with causion, you can't use response writer as you imagine.
 		ctx    context.Context
 		values ConnectionValues
-		server *server
+		server *Server
 		// #119 , websocket writers are not protected by locks inside the gorilla's websocket code
 		// so we must protect them otherwise we're getting concurrent connection error on multi writers in the same time.
 		writerMu sync.Mutex
@@ -221,7 +223,7 @@ type (
 
 var _ Connection = &connection{}
 
-func newConnection(ctx context.Context, s *server, underlineConn UnderlineConnection, id string) *connection {
+func newConnection(ctx context.Context, s *Server, underlineConn UnderlineConnection, id string) *connection {
 	c := &connection{
 		underline:                underlineConn,
 		id:                       id,
@@ -334,7 +336,7 @@ func (c *connection) startReader() {
 		_, data, err := conn.ReadMessage()
 		if err != nil {
 			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
-				c.FireStatusCode(err.Error())
+				c.FireOnError(err.Error())
 			}
 			break
 		} else {
@@ -397,6 +399,10 @@ func (c *connection) ID() string {
 	return c.id
 }
 
+func (c *connection) Server() *Server {
+	return c.server
+}
+
 func (c *connection) Context() context.Context {
 	return c.ctx
 }
@@ -415,11 +421,11 @@ func (c *connection) OnDisconnect(cb DisconnectFunc) {
 	c.onDisconnectListeners = append(c.onDisconnectListeners, cb)
 }
 
-func (c *connection) OnStatusCode(cb ErrorFunc) {
+func (c *connection) OnError(cb ErrorFunc) {
 	c.onErrorListeners = append(c.onErrorListeners, cb)
 }
 
-func (c *connection) FireStatusCode(errorMessage string) {
+func (c *connection) FireOnError(errorMessage string) {
 	for _, cb := range c.onErrorListeners {
 		cb(errorMessage)
 	}
diff --git a/websocket/emitter.go b/websocket/emitter.go
index 10f82e79..5827b24d 100644
--- a/websocket/emitter.go
+++ b/websocket/emitter.go
@@ -1,20 +1,10 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package websocket
 
-// -------------------------------------------------------------------------------------
-// -------------------------------------------------------------------------------------
-// --------------------------------Emitter implementation-------------------------------
-// -------------------------------------------------------------------------------------
-// -------------------------------------------------------------------------------------
-
 const (
 	// All is the string which the Emitter use to send a message to all
 	All = ""
 	// Broadcast is the string which the Emitter use to send a message to all except this connection
-	Broadcast = ";gowebsocket;to;all;except;me;"
+	Broadcast = ";ionwebsocket;to;all;except;me;"
 )
 
 type (
diff --git a/websocket/message.go b/websocket/message.go
index b02c9ce8..f5e4f29e 100644
--- a/websocket/message.go
+++ b/websocket/message.go
@@ -1,7 +1,3 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package websocket
 
 import (
@@ -15,16 +11,6 @@ import (
 	"github.com/valyala/bytebufferpool"
 )
 
-// -------------------------------------------------------------------------------------
-// -------------------------------------------------------------------------------------
-// -----------------websocket messages and de/serialization implementation--------------
-// -------------------------------------------------------------------------------------
-// -------------------------------------------------------------------------------------
-
-/*
-serializer, [de]websocketMessageSerialize the messages from the client to the websocketServer and from the websocketServer to the client
-*/
-
 // The same values are exists on client side also
 const (
 	websocketStringMessageType websocketMessageType = iota
diff --git a/websocket/server.go b/websocket/server.go
index 82cfa428..07abc08a 100644
--- a/websocket/server.go
+++ b/websocket/server.go
@@ -1,83 +1,13 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
 package websocket
 
 import (
 	"sync"
 
-	"github.com/gorilla/websocket"
-	"github.com/kataras/iris"
 	"github.com/kataras/iris/context"
+
+	"github.com/gorilla/websocket"
 )
 
-// Server is the websocket server,
-// listens on the config's port, the critical part is the event OnConnection
-type Server interface {
-	// Attach adapts the websocket server to an Iris instance.
-	// see websocket.go
-	Attach(app *iris.Application)
-
-	// Handler returns the iris.Handler
-	// which is setted to the 'Websocket Endpoint path',
-	// the client should target to this handler's developer's custom path
-	// ex: app.Any("/myendpoint", mywebsocket.Handler())
-	Handler() context.Handler
-
-	// OnConnection this is the main event you, as developer, will work with each of the websocket connections
-	OnConnection(cb ConnectionFunc)
-
-	/*
-	   connection actions, same as the connection's method,
-	    but these methods accept the connection ID,
-	    which is useful when the developer maps
-	    this id with a database field (using config.IDGenerator).
-	*/
-
-	// IsConnected returns true if the connection with that ID is connected to the server
-	// useful when you have defined a custom connection id generator (based on a database)
-	// and you want to check if that connection is already connected (on multiple tabs)
-	IsConnected(connID string) bool
-
-	// Join joins a websocket client to a room,
-	// first parameter is the room name and the second the connection.ID()
-	//
-	// You can use connection.Join("room name") instead.
-	Join(roomName string, connID string)
-
-	// LeaveAll kicks out a connection from ALL of its joined rooms
-	LeaveAll(connID string)
-
-	// Leave leaves a websocket client from a room,
-	// first parameter is the room name and the second the connection.ID()
-	//
-	// You can use connection.Leave("room name") instead.
-	// Returns true if the connection has actually left from the particular room.
-	Leave(roomName string, connID string) bool
-
-	// GetConnectionsByRoom returns a list of Connection
-	// are joined to this room.
-	GetConnectionsByRoom(roomName string) []Connection
-
-	// Disconnect force-disconnects a websocket connection
-	// based on its connection.ID()
-	// What it does?
-	// 1. remove the connection from the list
-	// 2. leave from all joined rooms
-	// 3. fire the disconnect callbacks, if any
-	// 4. close the underline connection and return its error, if any.
-	//
-	// You can use the connection.Disconnect() instead.
-	Disconnect(connID string) error
-}
-
-// -------------------------------------------------------------------------------------
-// -------------------------------------------------------------------------------------
-// --------------------------------Connection key-based list----------------------------
-// -------------------------------------------------------------------------------------
-// -------------------------------------------------------------------------------------
-
 type connectionKV struct {
 	key   string // the connection ID
 	value *connection
@@ -147,57 +77,79 @@ func (cs *connections) remove(key string) (*connection, bool) {
 	return nil, false
 }
 
-// -------------------------------------------------------------------------------------
-// -------------------------------------------------------------------------------------
-// --------------------------------Server implementation--------------------------------
-// -------------------------------------------------------------------------------------
-// -------------------------------------------------------------------------------------
-
 type (
-	// ConnectionFunc is the callback which fires when a client/connection is connected to the server.
+	// ConnectionFunc is the callback which fires when a client/connection is connected to the Server.
 	// Receives one parameter which is the Connection
 	ConnectionFunc func(Connection)
 
-	// websocketRoomPayload is used as payload from the connection to the server
+	// websocketRoomPayload is used as payload from the connection to the Server
 	websocketRoomPayload struct {
 		roomName     string
 		connectionID string
 	}
 
-	// payloads, connection -> server
+	// payloads, connection -> Server
 	websocketMessagePayload struct {
 		from string
 		to   string
 		data []byte
 	}
 
-	server struct {
+	// Server is the websocket Server's implementation.
+	//
+	// It listens for websocket clients (either from the javascript client-side or from any websocket implementation).
+	// See `OnConnection` , to register a single event which will handle all incoming connections and
+	// the `Handler` which builds the upgrader handler that you can register to a route based on an Endpoint.
+	//
+	// To serve the built'n javascript client-side library look the `websocket.ClientHandler`.
+	Server struct {
 		config                Config
 		connections           connections
 		rooms                 map[string][]string // by default a connection is joined to a room which has the connection id as its name
 		mu                    sync.Mutex          // for rooms
 		onConnectionListeners []ConnectionFunc
-		//connectionPool        *sync.Pool // sadly I can't make this because the websocket connection is live until is closed.
+		//connectionPool        sync.Pool // sadly we can't make this because the websocket connection is live until is closed.
+		handler context.Handler
 	}
 )
 
-var _ Server = &server{}
+// New returns a new websocket Server based on a configuration.
+// See `OnConnection` , to register a single event which will handle all incoming connections and
+// the `Handler` which builds the upgrader handler that you can register to a route based on an Endpoint.
+//
+// To serve the built'n javascript client-side library look the `websocket.ClientHandler`.
+func New(cfg Config) *Server {
+	return &Server{
+		config: cfg.Validate(),
+		rooms:  make(map[string][]string, 0),
+		onConnectionListeners: make([]ConnectionFunc, 0),
+	}
+}
 
-// server implementation
-
-func (s *server) Handler() context.Handler {
+// Handler builds the handler based on the configuration and returns it.
+// It should be called once per Server, its result should be passed
+// as a middleware to an iris route which will be responsible
+// to register the websocket's endpoint.
+//
+// Endpoint is the path which the websocket Server will listen for clients/connections.
+//
+// To serve the built'n javascript client-side library look the `websocket.ClientHandler`.
+func (s *Server) Handler() context.Handler {
 	// build the upgrader once
 	c := s.config
 
 	upgrader := websocket.Upgrader{
-		ReadBufferSize:  c.ReadBufferSize,
-		WriteBufferSize: c.WriteBufferSize,
-		Error:           c.Error,
-		CheckOrigin:     c.CheckOrigin,
-		Subprotocols:    c.Subprotocols,
+		HandshakeTimeout:  c.HandshakeTimeout,
+		ReadBufferSize:    c.ReadBufferSize,
+		WriteBufferSize:   c.WriteBufferSize,
+		Error:             c.Error,
+		CheckOrigin:       c.CheckOrigin,
+		Subprotocols:      c.Subprotocols,
+		EnableCompression: c.EnableCompression,
 	}
+
 	return func(ctx context.Context) {
-		// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
+		// Upgrade upgrades the HTTP Server connection to the WebSocket protocol.
 		//
 		// The responseHeader is included in the response to the client's upgrade
 		// request. Use the responseHeader to specify cookies (Set-Cookie) and the
@@ -207,8 +159,8 @@ func (s *server) Handler() context.Handler {
 		// response.
 		conn, err := upgrader.Upgrade(ctx.ResponseWriter(), ctx.Request(), ctx.ResponseWriter().Header())
 		if err != nil {
-			ctx.Application().Log("websocket error: %v", err)
-			ctx.StatusCode(iris.StatusServiceUnavailable)
+			ctx.Application().Logger().Warnf("websocket error: %v\n", err)
+			ctx.StatusCode(503) // Status Service Unavailable
 			return
 		}
 		s.handleConnection(ctx, conn)
@@ -216,12 +168,12 @@ func (s *server) Handler() context.Handler {
 }
 
 // handleConnection creates & starts to listening to a new connection
-func (s *server) handleConnection(ctx context.Context, websocketConn UnderlineConnection) {
+func (s *Server) handleConnection(ctx context.Context, websocketConn UnderlineConnection) {
 	// use the config's id generator (or the default) to create a websocket client/connection id
 	cid := s.config.IDGenerator(ctx)
 	// create the new connection
 	c := newConnection(ctx, s, websocketConn, cid)
-	// add the connection to the server's list
+	// add the connection to the Server's list
 	s.connections.add(cid, c)
 
 	// join to itself
@@ -230,7 +182,7 @@ func (s *server) handleConnection(ctx context.Context, websocketConn UnderlineCo
 	// NOTE TO ME: fire these first BEFORE startReader and startPinger
 	// in order to set the events and any messages to send
 	// the startPinger will send the OK to the client and only
-	// then the client is able to send and receive from server
+	// then the client is able to send and receive from Server
 	// when all things are ready and only then. DO NOT change this order.
 
 	// fire the on connection event callbacks, if any
@@ -251,25 +203,35 @@ func (s *server) handleConnection(ctx context.Context, websocketConn UnderlineCo
 	 his/her websocket connections without even use the connection itself.
 
 	 Another question may be:
-	 Q: Why you use server as the main actioner for all of the connection actions?
-	 	  For example the server.Disconnect(connID) manages the connection internal fields, is this code-style correct?
-	 A: It's the correct code-style for these type of applications and libraries, server manages all, the connnection's functions
-	 should just do some internal checks (if needed) and push the action to its parent, which is the server, the server is able to
+	 Q: Why you use Server as the main actioner for all of the connection actions?
+	 	  For example the Server.Disconnect(connID) manages the connection internal fields, is this code-style correct?
+	 A: It's the correct code-style for these type of applications and libraries, Server manages all, the connnection's functions
+	 should just do some internal checks (if needed) and push the action to its parent, which is the Server, the Server is able to
 	 remove a connection, the rooms of its connected and all these things, so in order to not split the logic, we have the main logic
-	 here, in the server, and let the connection with some exported functions whose exists for the per-connection action user's code-style.
+	 here, in the Server, and let the connection with some exported functions whose exists for the per-connection action user's code-style.
 
 	 Ok my english are s** I can feel it, but these comments are mostly for me.
 */
 
-// OnConnection this is the main event you, as developer, will work with each of the websocket connections
-func (s *server) OnConnection(cb ConnectionFunc) {
+/*
+   connection actions, same as the connection's method,
+    but these methods accept the connection ID,
+    which is useful when the developer maps
+    this id with a database field (using config.IDGenerator).
+*/
+
+// OnConnection is the main event you, as developer, will work with each of the websocket connections.
+func (s *Server) OnConnection(cb ConnectionFunc) {
+	if s.handler == nil {
+
+	}
 	s.onConnectionListeners = append(s.onConnectionListeners, cb)
 }
 
-// IsConnected returns true if the connection with that ID is connected to the server
+// IsConnected returns true if the connection with that ID is connected to the Server
 // useful when you have defined a custom connection id generator (based on a database)
 // and you want to check if that connection is already connected (on multiple tabs)
-func (s *server) IsConnected(connID string) bool {
+func (s *Server) IsConnected(connID string) bool {
 	c := s.connections.get(connID)
 	return c != nil
 }
@@ -278,14 +240,14 @@ func (s *server) IsConnected(connID string) bool {
 // first parameter is the room name and the second the connection.ID()
 //
 // You can use connection.Join("room name") instead.
-func (s *server) Join(roomName string, connID string) {
+func (s *Server) Join(roomName string, connID string) {
 	s.mu.Lock()
 	s.join(roomName, connID)
 	s.mu.Unlock()
 }
 
 // join used internally, no locks used.
-func (s *server) join(roomName string, connID string) {
+func (s *Server) join(roomName string, connID string) {
 	if s.rooms[roomName] == nil {
 		s.rooms[roomName] = make([]string, 0)
 	}
@@ -293,7 +255,7 @@ func (s *server) join(roomName string, connID string) {
 }
 
 // LeaveAll kicks out a connection from ALL of its joined rooms
-func (s *server) LeaveAll(connID string) {
+func (s *Server) LeaveAll(connID string) {
 	s.mu.Lock()
 	for name, connectionIDs := range s.rooms {
 		for i := range connectionIDs {
@@ -314,7 +276,7 @@ func (s *server) LeaveAll(connID string) {
 //
 // You can use connection.Leave("room name") instead.
 // Returns true if the connection has actually left from the particular room.
-func (s *server) Leave(roomName string, connID string) bool {
+func (s *Server) Leave(roomName string, connID string) bool {
 	s.mu.Lock()
 	left := s.leave(roomName, connID)
 	s.mu.Unlock()
@@ -322,7 +284,7 @@ func (s *server) Leave(roomName string, connID string) bool {
 }
 
 // leave used internally, no locks used.
-func (s *server) leave(roomName string, connID string) (left bool) {
+func (s *Server) leave(roomName string, connID string) (left bool) {
 	///THINK: we could add locks to its room but we still use the lock for the whole rooms or we can just do what we do with connections
 	// I will think about it on the next revision, so far we use the locks only for rooms so we are ok...
 	if s.rooms[roomName] != nil {
@@ -348,7 +310,7 @@ func (s *server) leave(roomName string, connID string) (left bool) {
 
 // GetConnectionsByRoom returns a list of Connection
 // which are joined to this room.
-func (s *server) GetConnectionsByRoom(roomName string) []Connection {
+func (s *Server) GetConnectionsByRoom(roomName string) []Connection {
 	s.mu.Lock()
 	var conns []Connection
 	if connIDs, found := s.rooms[roomName]; found {
@@ -370,7 +332,7 @@ func (s *server) GetConnectionsByRoom(roomName string) []Connection {
 //
 // You SHOULD use connection.EmitMessage/Emit/To().Emit/EmitMessage instead.
 // let's keep it unexported for the best.
-func (s *server) emitMessage(from, to string, data []byte) {
+func (s *Server) emitMessage(from, to string, data []byte) {
 	if to != All && to != Broadcast && s.rooms[to] != nil {
 		// it suppose to send the message to a specific room/or a user inside its own room
 		for _, connectionIDInsideRoom := range s.rooms[to] {
@@ -410,7 +372,7 @@ func (s *server) emitMessage(from, to string, data []byte) {
 // 4. close the underline connection and return its error, if any.
 //
 // You can use the connection.Disconnect() instead.
-func (s *server) Disconnect(connID string) (err error) {
+func (s *Server) Disconnect(connID string) (err error) {
 	// leave from all joined rooms before remove the actual connection from the list.
 	// note: we cannot use that to send data if the client is actually closed.
 	s.LeaveAll(connID)
diff --git a/websocket/websocket.go b/websocket/websocket.go
index 246633bb..20f4b58a 100644
--- a/websocket/websocket.go
+++ b/websocket/websocket.go
@@ -1,63 +1,74 @@
-// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+/*Package websocket provides rich websocket support for the iris web framework.
 
+Source code and other details for the project are available at GitHub:
+
+   https://github.com/kataras/iris/tree/master/websocket
+
+Installation
+
+    $ go get -u github.com/kataras/iris/websocket
+
+
+Example code:
+
+
+	package main
+
+	import (
+		"fmt"
+
+		"github.com/kataras/iris"
+		"github.com/kataras/iris/context"
+
+		"github.com/kataras/iris/websocket"
+	)
+
+	func main() {
+		app := iris.New()
+
+		app.Get("/", func(ctx context.Context) {
+			ctx.ServeFile("websockets.html", false)
+		})
+
+		setupWebsocket(app)
+
+		// x2
+		// http://localhost:8080
+		// http://localhost:8080
+		// write something, press submit, see the result.
+		app.Run(iris.Addr(":8080"))
+	}
+
+	func setupWebsocket(app *iris.Application) {
+		// create our echo websocket server
+		ws := websocket.New(websocket.Config{
+			ReadBufferSize:  1024,
+			WriteBufferSize: 1024,
+		})
+		ws.OnConnection(handleConnection)
+
+		// register the server's endpoint.
+		// see the inline javascript code in the websockets.html,
+		// this endpoint is used to connect to the server.
+		app.Get("/echo", ws.Handler())
+
+		// serve the javascript built'n client-side library,
+		// see weboskcets.html script tags, this path is used.
+		app.Any("/iris-ws.js", func(ctx context.Context) {
+			ctx.Write(websocket.ClientSource)
+		})
+	}
+
+	func handleConnection(c websocket.Connection) {
+		// Read events from browser
+		c.On("chat", func(msg string) {
+			// Print the message to the console
+			fmt.Printf("%s sent: %s\n", c.Context().RemoteAddr(), msg)
+			// Write message back to the client message owner:
+			// c.Emit("chat", msg)
+			c.To(websocket.Broadcast).Emit("chat", msg)
+		})
+	}
+
+*/
 package websocket
-
-import (
-	"strings"
-
-	"github.com/kataras/iris"
-)
-
-// New returns a new websocket server policy adaptor.
-func New(cfg Config) Server {
-	return &server{
-		config: cfg.Validate(),
-		rooms:  make(map[string][]string, 0),
-		onConnectionListeners: make([]ConnectionFunc, 0),
-	}
-}
-
-func fixPath(s string) string {
-	if s == "" {
-		return ""
-	}
-
-	if s[0] != '/' {
-		s = "/" + s
-	}
-
-	s = strings.Replace(s, "//", "/", -1)
-	return s
-}
-
-// Attach adapts the websocket server to one or more Iris instances.
-func (s *server) Attach(app *iris.Application) {
-	wsPath := fixPath(s.config.Endpoint)
-	if wsPath == "" {
-		app.Log("websocket's configuration field 'Endpoint' cannot be empty, websocket server stops")
-		return
-	}
-
-	wsClientSidePath := fixPath(s.config.ClientSourcePath)
-	if wsClientSidePath == "" {
-		app.Log("websocket's configuration field 'ClientSourcePath' cannot be empty, websocket server stops")
-		return
-	}
-
-	// set the routing for client-side source (javascript) (optional)
-	clientSideLookupName := "iris-websocket-client-side"
-	wsHandler := s.Handler()
-	app.Get(wsPath, wsHandler)
-	// check if client side doesn't already exists
-	if app.GetRoute(clientSideLookupName) == nil {
-		// serve the client side on domain:port/iris-ws.js
-		r, err := app.StaticContent(wsClientSidePath, "application/javascript", ClientSource)
-		if err != nil {
-			app.Log("websocket's route for javascript client-side library failed with: %v", err)
-			return
-		}
-		r.Name = clientSideLookupName
-	}
-}