1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-17 18:07:01 +00:00
Read HISTORY.md
This commit is contained in:
Gerasimos Maropoulos
2016-09-27 16:28:38 +03:00
parent 70d81e6843
commit 5c98c5a493
16 changed files with 1160 additions and 2273 deletions

View File

@@ -1,6 +1,57 @@
# History
**How to upgrade**: remove your `$GOPATH/src/github.com/kataras/iris` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris`.
**How to upgrade**: remove your `$GOPATH/src/github.com/kataras` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`.
## 4.3.0 -> 4.4.0
**NOTE**: For normal users this update offers nothing, read that only if you run Iris behind a proxy or balancer like `nginx` or you need to serve using a custom `net.Listener`.
This update implements the [support of using native servers and net.Listener instead of Iris' defined](https://github.com/kataras/iris/issues/438).
### Breaking changes
- `iris.Config.Profile` field removed and the whole pprof transfered to the [iris-contrib/middleware/pprof](https://github.com/iris-contrib/middleware/tree/master/pprof).
- `iris.ListenTLSAuto` renamed to `iris.ListenLETSENCRYPT`
- `iris.Default.Handler` is `iris.Router` which is the Handler you can use to setup a custom router or bind Iris' router, after `iris.Build` call, to an external custom server.
- `iris.ServerConfiguration`, `iris.ListenVirtual`, `iris.AddServer`, `iris.Go` & `iris.TesterConfiguration.ListeningAddr` removed, read below the reason and their alternatives
### New features
- Boost Performance on server's startup
- **NEW**: `iris.Reserve()` re-starts the server if `iris.Close()` called previously.
- **NEW**: `iris.Config.VHost` and `iris.Config.VScheme` replaces the previous `ListenVirtual`, `iris.TesterConfiguration.ListeningAddr`, `iris.ServerConfiguration.VListeningAddr`, `iris.ServerConfiguration.VScheme`.
- **NEW**: `iris.Build` it's called automatically on Listen functions or Serve function. **CALL IT, MANUALLY, ONLY** WHEN YOU WANT TO BE ABLE TO GET THE IRIS ROUTER(`iris.Router`) AND PASS THAT, HANDLER, TO ANOTHER EXTERNAL FASTHTTP SERVER.
- **NEW**: `iris.Serve(net.Listener)`. Starts the server using a custom net.Listener, look below for example link
- **NEW**: now that iris supports custom net.Listener bind, I had to provide to you some net.Listeners too, such as `iris.TCP4`, `iris.UNIX`, `iris.TLS` , `iris.LETSENCRPYPT` & `iris.CERT` , all of these are optionals because you can just use the `iris.Listen`, `iris.ListenUNIX`, `iris.ListenTLS` & `iris.ListenLETSENCRYPT`, but if you want, for example, to pass your own `tls.Config` then you will have to create a custom net.Listener and pass that to the `iris.Serve(net.Listener)`.
With these in mind, developers are now able to fill their advanced needs without use the `iris.AddServer, ServerConfiguration and V fields`, so it's easier to:
- use any external (fasthttp compatible) server or router. Examples: [server](https://github.com/iris-contrib/tree/master/custom_fasthtthttp_server) and [router]((https://github.com/iris-contrib/tree/master/custom_fasthtthttp_router)
- bind any `net.Listener` which will be used to run the Iris' HTTP server, as requested [here](https://github.com/kataras/iris/issues/395). Example [here](https://github.com/iris-contrib/tree/master/custom_net_listener)
- setup virtual host and scheme, useful when you run Iris behind `nginx` (etc) and want template function `{{url }}` and subdomains to work as you expected. Usage:
```go
iris.Config.VHost = "mydomain.com"
iris.Config.VScheme = "https://"
iris.Listen(":8080")
// this will run on localhost:8080 but templates, subdomains and all that will act like https://mydomain.com,
// before this update you used the iris.AddServer and iris.Go and pass some strange fields into
```
Last, for testers:
Who used the `iris.ListenVirtual(...).Handler`:
If closed server, then `iris.Build()` and `iris.Router`, otherwise just `iris.Router`.
To test subdomains or a custom domain just set the `iris.Config.VHost` and `iris.Config.VScheme` fields, instead of the old `subdomain_test_handler := iris.AddServer(iris.ServerConfiguration{VListeningAddr:"...", Virtual: true, VScheme:false}).Handler`. Usage [here](https://github.com/kataras/blob/master/http_test.go).
**Finally**, I have to notify you that [examples](https://github.com/iris-contrib/examples), [plugins](https://github.com/iris-contrib/plugin), [middleware](https://github.com/iris-contrib/middleware) and [book](https://github.com/iris-contrib/gitbook) have been updated.
## 4.2.9 -> 4.3.0

704
LICENSE
View File

@@ -1,25 +1,7 @@
The MIT License (MIT)
Copyright (c) 2016 Gerasimos Maropoulos
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Third-party packages licenses,
fasthttp -
The MIT License (MIT)
Copyright (c) 2015-2016 Aliaksandr Valialkin, VertaMedia
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
@@ -37,685 +19,3 @@ 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.
django -
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.
handlebars -
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.
Jade -
Copyright (c) 2015, Joker.
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.
Blackfriday -
is distributed under the Simplified BSD License:
Copyright © 2011 Russ Ross
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. 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.
klaouspost/compress -
Copyright (c) 2012 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
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
sourcecode: http.go->type->methods->muxEntry.add/.get/.giveProcedureTo -
Copyright (c) 2013 Julien Schmidt
Copyright (c) 2016 Gerasimos Maropoulos - performance improvements
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.
* The names of the contributors may not 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 JULIEN SCHMIDT 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 (MIT)
color
Copyright (c) 2016 Gerasimos Maropoulos
Copyright (c) 2013 Fatih Arslan
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.
go-colorable - The MIT License (MIT)
Copyright (c) 2016 Yasuhiro Matsumoto
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.
mergo - Copyright (c) 2013 Dario Castañé. All rights reserved.
mergo - Copyright (c) 2012 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
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
formam - Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
i18n - Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
httpexpect - test framework,
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.
Go packages license,
Copyright (c) 2012 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
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -11,7 +11,7 @@
<a href="#"><img src="https://img.shields.io/badge/platform-Any-ec2eb4.svg?style=flat-square" alt="Platforms"></a>
<a href="https://github.com/kataras/iris/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0%20%20-E91E63.svg?style=flat-square" alt="License"></a>
<a href="https://github.com/kataras/iris/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT%20%20-E91E63.svg?style=flat-square" alt="License"></a>
<a href="https://golang.org"><img src="https://img.shields.io/badge/powered_by-Go-3362c2.svg?style=flat-square" alt="Built with GoLang"></a>
@@ -19,7 +19,7 @@
<br/>
<a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%204.3.0%20-blue.svg?style=flat-square" alt="Releases"></a>
<a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%204.4.0%20-blue.svg?style=flat-square" alt="Releases"></a>
<a href="https://github.com/iris-contrib/examples"><img src="https://img.shields.io/badge/%20examples-repository-3362c2.svg?style=flat-square" alt="Examples"></a>
@@ -117,16 +117,14 @@ If you'd like to discuss this package, or ask questions about it, feel free to
* [Chat][Chat].
New website-docs & logo have been designed by the community[*](https://github.com/kataras/iris/issues/153)
- Website created by [@kujtimiihoxha](https://github.com/kujtimiihoxha)
- Logo designed by [@OneebMalik](https://github.com/OneebMalik)
New logo have been designed by the community member, [@OneebMalik](https://github.com/OneebMalik).
Features
------------
- Focus on high performance
- Robust routing, static, wildcard subdomains and routes.
- Internal version checker & updater[.*](https://github.com/kataras/iris/issues/401).
- [Websocket API](https://github.com/kataras/go-websocket), [Sessions](https://github.com/kataras/go-sessions) support out of the box
- Remote control through [SSH](https://github.com/iris-contrib/examples/blob/master/ssh/main.go)
- View system supporting [6+](https://github.com/kataras/go-template) template engines.[*](https://kataras.gitbooks.io/iris/content/template-engines.html)
@@ -160,6 +158,7 @@ Features
| [I18n Middleware ](https://github.com/iris-contrib/middleware/tree/master/i18n) | Simple internationalization | [example](https://github.com/iris-contrib/examples/tree/master/middleware_internationalization_i18n), [book section](https://kataras.gitbooks.io/iris/content/middleware-internationalization-and-localization.html) |
| [Recovery Middleware ](https://github.com/iris-contrib/middleware/tree/master/recovery) | Safety recover the station from panic | [example](https://github.com/iris-contrib/examples/blob/master/middleware_recovery/main.go) |
| [Logger Middleware ](https://github.com/iris-contrib/middleware/tree/master/logger) | Logs every request | [example](https://github.com/iris-contrib/examples/blob/master/middleware_logger/main.go), [book section](https://kataras.gitbooks.io/iris/content/logger.html) |
| [Profile Middleware ](https://github.com/iris-contrib/middleware/tree/master/pprof) | Http profiling for debugging | [example](https://github.com/iris-contrib/examples/blob/master/middleware_pprof/main.go) |
| [Editor Plugin](https://github.com/iris-contrib/plugin/tree/master/editor) | Alm-tools, a typescript online IDE/Editor | [book section](https://kataras.gitbooks.io/iris/content/plugin-editor.html) |
| [Typescript Plugin](https://github.com/iris-contrib/plugin/tree/master/typescript) | Auto-compile client-side typescript files | [book section](https://kataras.gitbooks.io/iris/content/plugin-typescript.html) |
| [OAuth,OAuth2 Plugin](https://github.com/iris-contrib/plugin/tree/master/oauth) | User Authentication was never be easier, supports >27 providers | [example](https://github.com/iris-contrib/examples/tree/master/plugin_oauth_oauth2), [book section](https://kataras.gitbooks.io/iris/content/plugin-oauth.html) |
@@ -193,13 +192,12 @@ This Benchmark test aims to compare the whole HTTP request processing between Go
Testing
------------
Community should write third-party or iris base tests to the [iris-contrib/tests repository](https://github.com/iris-contrib/tests).
I recommend writing your API tests using this new library, [httpexpect](https://github.com/gavv/httpexpect) which supports Iris and fasthttp now, after my request [here](https://github.com/gavv/httpexpect/issues/2).
I recommend writing your API tests using this new library, [httpexpect](https://github.com/gavv/httpexpect) which supports Iris and fasthttp now, after my request [here](https://github.com/gavv/httpexpect/issues/2). You can find Iris examples [here](https://github.com/gavv/httpexpect/blob/master/example/iris_test.go), [here](https://github.com/kataras/iris/blob/master/http_test.go) and [here](https://github.com/kataras/iris/blob/master/context_test.go).
Versioning
------------
Current: **v4.3.0**
Current: **v4.4.0**
> Iris is an active project
@@ -214,6 +212,7 @@ Todo
- [x] Use of the standard `log.Logger` instead of the `iris-contrib/logger`(colorful logger), make these changes to all middleware, examples and plugins.
- [x] Implement, even, a better way to manage configuration/options, devs will be able to set their own custom options inside there. ` I'm thinking of something the last days, but it will have breaking changes. `
- [x] Implement an internal updater, as requested [here](https://github.com/kataras/iris/issues/401).
- [x] Support of using native servers and net.Listener instead of Iris' defined.[*](https://github.com/kataras/iris/issues/438)
Iris is a **Community-Driven** Project, waiting for your suggestions and [feature requests](https://github.com/kataras/iris/issues?utf8=%E2%9C%93&q=label%3A%22feature%20request%22)!
@@ -231,26 +230,8 @@ If you are interested in contributing to the Iris project, please see the docume
License
------------
This project is licensed under the Apache License, Version 2.0.
This project is licensed under the MIT License, Copyright (c) 2016 Gerasimos Maropoulos.
License can be found [here](LICENSE).
[Travis Widget]: https://img.shields.io/travis/kataras/iris.svg?style=flat-square
[Travis]: http://travis-ci.org/kataras/iris
[License Widget]: https://img.shields.io/badge/license-Apache%202.0%20%20-E91E63.svg?style=flat-square
[License]: https://github.com/kataras/iris/blob/master/LICENSE
[Release Widget]: https://img.shields.io/badge/release-v4.3.0-blue.svg?style=flat-square
[Release]: https://github.com/kataras/iris/releases
[Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square
[Chat]: https://kataras.rocket.chat/channel/iris
[ChatMain]: https://kataras.rocket.chat/channel/iris
[ChatAlternative]: https://gitter.im/kataras/iris
[Report Widget]: https://img.shields.io/badge/report%20card-A%2B-F44336.svg?style=flat-square
[Report]: http://goreportcard.com/report/kataras/iris
[Documentation Widget]: https://img.shields.io/badge/documentation-reference-5272B4.svg?style=flat-square
[Documentation]: https://www.gitbook.com/book/kataras/iris/details
[Examples Widget]: https://img.shields.io/badge/examples-repository-3362c2.svg?style=flat-square
[Examples]: https://github.com/iris-contrib/examples
[Language Widget]: https://img.shields.io/badge/powered_by-Go-3362c2.svg?style=flat-square
[Language]: http://golang.org
[Platform Widget]: https://img.shields.io/badge/platform-Any--OS-gray.svg?style=flat-square

View File

@@ -8,7 +8,6 @@ import (
"io"
"os"
"strconv"
"strings"
"time"
)
@@ -41,6 +40,78 @@ func (o OptionSet) Set(c *Configuration) {
//
// Configuration is also implements the OptionSet so it's a valid option itself, this is briliant enough
type Configuration struct {
// VHost is the addr or the domain that server listens to, which it's optional
// When to set VHost manually:
// 1. it's automatically setted when you're calling
// $instance.Listen/ListenUNIX/ListenTLS/ListenLETSENCRYPT functions or
// ln,_ := iris.TCP4/UNIX/TLS/LETSENCRYPT; $instance.Serve(ln)
// 2. If you using a balancer, or something like nginx
// then set it in order to have the correct url
// when calling the template helper '{{url }}'
// *keep note that you can use {{urlpath }}) instead*
//
// Note: this is the main's server Host, you can setup unlimited number of fasthttp servers
// listening to the $instance.Handler after the manually-called $instance.Build
//
// Default comes from iris.Listen/.Serve with iris' listeners (iris.TCP4/UNIX/TLS/LETSENCRYPT)
VHost string
// VScheme is the scheme (http:// or https://) putted at the template function '{{url }}'
// It's an optional field,
// When to set VScheme manually:
// 1. You didn't start the main server using $instance.Listen/ListenTLS/ListenLETSENCRYPT or $instance.Serve($instance.TCP4()/.TLS...)
// 2. if you're using something like nginx and have iris listening with addr only(http://) but the nginx mapper is listening to https://
//
// Default comes from iris.Listen/.Serve with iris' listeners (TCP4,UNIX,TLS,LETSENCRYPT)
VScheme string
// MaxRequestBodySize Maximum request body size.
//
// The server rejects requests with bodies exceeding this limit.
//
// By default request body size is 8MB.
MaxRequestBodySize int
// Per-connection buffer size for requests' reading.
// This also limits the maximum header size.
//
// Increase this buffer if your clients send multi-KB RequestURIs
// and/or multi-KB headers (for example, BIG cookies).
//
// Default buffer size is used if not set.
ReadBufferSize int
// Per-connection buffer size for responses' writing.
//
// Default buffer size is used if not set.
WriteBufferSize int
// Maximum duration for reading the full request (including body).
//
// This also limits the maximum duration for idle keep-alive
// connections.
//
// By default request read timeout is unlimited.
ReadTimeout time.Duration
// Maximum duration for writing the full response (including body).
//
// By default response write timeout is unlimited.
WriteTimeout time.Duration
// Maximum number of concurrent client connections allowed per IP.
//
// By default unlimited number of concurrent connections
MaxConnsPerIP int
// Maximum number of requests served per connection.
//
// The server closes connection after the last request.
// 'Connection: close' header is added to the last response.
//
// By default unlimited number of requests may be served per connection.
MaxRequestsPerConn int
// CheckForUpdates will try to search for newer version of Iris based on the https://github.com/kataras/iris/releases
// If a newer version found then the app will ask the he dev/user if want to update the 'x' version
// if 'y' is pressed then the updater will try to install the latest version
@@ -102,35 +173,6 @@ type Configuration struct {
// Default is [IRIS]
LoggerPreffix string
// ProfilePath a the route path, set it to enable http pprof tool
// Default is empty, if you set it to a $path, these routes will handled:
// $path/cmdline
// $path/profile
// $path/symbol
// $path/goroutine
// $path/heap
// $path/threadcreate
// $path/pprof/block
// for example if '/debug/pprof'
// http://yourdomain:PORT/debug/pprof/
// http://yourdomain:PORT/debug/pprof/cmdline
// http://yourdomain:PORT/debug/pprof/profile
// http://yourdomain:PORT/debug/pprof/symbol
// http://yourdomain:PORT/debug/pprof/goroutine
// http://yourdomain:PORT/debug/pprof/heap
// http://yourdomain:PORT/debug/pprof/threadcreate
// http://yourdomain:PORT/debug/pprof/pprof/block
// it can be a subdomain also, for example, if 'debug.'
// http://debug.yourdomain:PORT/
// http://debug.yourdomain:PORT/cmdline
// http://debug.yourdomain:PORT/profile
// http://debug.yourdomain:PORT/symbol
// http://debug.yourdomain:PORT/goroutine
// http://debug.yourdomain:PORT/heap
// http://debug.yourdomain:PORT/threadcreate
// http://debug.yourdomain:PORT/pprof/block
ProfilePath string
// DisableTemplateEngines set to true to disable loading the default template engine (html/template) and disallow the use of iris.UseEngine
// default is false
DisableTemplateEngines bool
@@ -176,6 +218,116 @@ func (c Configuration) Set(main *Configuration) {
// All options starts with "Option" preffix in order to be easier to find what dev searching for
var (
// OptionVHost is the addr or the domain that server listens to, which it's optional
// When to set VHost manually:
// 1. it's automatically setted when you're calling
// $instance.Listen/ListenUNIX/ListenTLS/ListenLETSENCRYPT functions or
// ln,_ := iris.TCP4/UNIX/TLS/LETSENCRYPT; $instance.Serve(ln)
// 2. If you using a balancer, or something like nginx
// then set it in order to have the correct url
// when calling the template helper '{{url }}'
// *keep note that you can use {{urlpath }}) instead*
//
// Note: this is the main's server Host, you can setup unlimited number of fasthttp servers
// listening to the $instance.Handler after the manually-called $instance.Build
//
// Default comes from iris.Listen/.Serve with iris' listeners (iris.TCP4/UNIX/TLS/LETSENCRYPT)
OptionVHost = func(val string) OptionSet {
return func(c *Configuration) {
c.VHost = val
}
}
// OptionVScheme is the scheme (http:// or https://) putted at the template function '{{url }}'
// It's an optional field,
// When to set Scheme manually:
// 1. You didn't start the main server using $instance.Listen/ListenTLS/ListenLETSENCRYPT or $instance.Serve($instance.TCP4()/.TLS...)
// 2. if you're using something like nginx and have iris listening with addr only(http://) but the nginx mapper is listening to https://
//
// Default comes from iris.Listen/.Serve with iris' listeners (TCP4,UNIX,TLS,LETSENCRYPT)
OptionVScheme = func(val string) OptionSet {
return func(c *Configuration) {
c.VScheme = val
}
}
// OptionMaxRequestBodySize Maximum request body size.
//
// The server rejects requests with bodies exceeding this limit.
//
// By default request body size is 8MB.
OptionMaxRequestBodySize = func(val int) OptionSet {
return func(c *Configuration) {
c.MaxRequestBodySize = val
}
}
// Per-connection buffer size for requests' reading.``
// This also limits the maximum header size.
//
// Increase this buffer if your clients send multi-KB RequestURIs
// and/or multi-KB headers (for example, BIG cookies).
//
// Default buffer size is used if not set.
OptionReadBufferSize = func(val int) OptionSet {
return func(c *Configuration) {
c.ReadBufferSize = val
}
}
// Per-connection buffer size for responses' writing.
//
// Default buffer size is used if not set.
OptionWriteBufferSize = func(val int) OptionSet {
return func(c *Configuration) {
c.WriteBufferSize = val
}
}
// Maximum duration for reading the full request (including body).
//
// This also limits the maximum duration for idle keep-alive
// connections.
//
// By default request read timeout is unlimited.
OptionReadTimeout = func(val time.Duration) OptionSet {
return func(c *Configuration) {
c.ReadTimeout = val
}
}
// Maximum duration for writing the full response (including body).
//
// By default response write timeout is unlimited.
OptionWriteTimeout = func(val time.Duration) OptionSet {
return func(c *Configuration) {
c.WriteTimeout = val
}
}
// OptionMaxConnsPerIP Maximum number of concurrent client connections allowed per IP.
//
// By default unlimited number of concurrent connections
// may be established to the server from a single IP address.
OptionMaxConnsPerIP = func(val int) OptionSet {
return func(c *Configuration) {
c.MaxConnsPerIP = val
}
}
// OptionMaxRequestsPerConn Maximum number of requests served per connection.
//
// The server closes connection after the last request.
// 'Connection: close' header is added to the last response.
//
// By default unlimited number of requests may be served per connection.
OptionMaxRequestsPerConn = func(val int) OptionSet {
return func(c *Configuration) {
c.MaxRequestsPerConn = val
}
}
// OptionCheckForUpdates will try to search for newer version of Iris based on the https://github.com/kataras/iris/releases
// If a newer version found then the app will ask the he dev/user if want to update the 'x' version
// if 'y' is pressed then the updater will try to install the latest version
@@ -194,7 +346,6 @@ var (
return func(c *Configuration) {
c.CheckForUpdates = val
}
}
// CheckForUpdatesSync checks for updates before server starts, it will have a little delay depends on the machine's download's speed
// See CheckForUpdates for more
@@ -256,14 +407,6 @@ var (
}
}
// OptionProfilePath a the route path, set it to enable http pprof tool
// Default is empty, if you set it to a $path, these routes will handled:
OptionProfilePath = func(val string) OptionSet {
return func(c *Configuration) {
c.ProfilePath = val
}
}
// OptionDisableTemplateEngines set to true to disable loading the default template engine (html/template) and disallow the use of iris.UseEngine
// Default is false
OptionDisableTemplateEngines = func(val bool) OptionSet {
@@ -340,6 +483,25 @@ const (
DefaultDisablePathEscape = false
DefaultCharset = "UTF-8"
DefaultLoggerPreffix = "[IRIS] "
// DefaultMaxRequestBodySize is 8MB
DefaultMaxRequestBodySize = 2 * fasthttp.DefaultMaxRequestBodySize
// Per-connection buffer size for requests' reading.
// This also limits the maximum header size.
//
// Increase this buffer if your clients send multi-KB RequestURIs
// and/or multi-KB headers (for example, BIG cookies).
//
// Default buffer size is 8MB
DefaultReadBufferSize = 8096
// Per-connection buffer size for responses' writing.
//
// Default buffer size is 8MB
DefaultWriteBufferSize = 8096
// DefaultServerName the response header of the 'Server' value when writes to the client
DefaultServerName = "iris"
)
var (
@@ -350,6 +512,13 @@ var (
// DefaultConfiguration returns the default configuration for an Iris station, fills the main Configuration
func DefaultConfiguration() Configuration {
return Configuration{
VHost: "",
VScheme: "",
MaxRequestBodySize: DefaultMaxRequestBodySize,
ReadBufferSize: DefaultReadBufferSize,
WriteBufferSize: DefaultWriteBufferSize,
MaxConnsPerIP: 0,
MaxRequestsPerConn: 0,
CheckForUpdates: false,
CheckForUpdatesSync: false,
DisablePathCorrection: DefaultDisablePathCorrection,
@@ -362,7 +531,6 @@ func DefaultConfiguration() Configuration {
TimeFormat: DefaultTimeFormat,
Charset: DefaultCharset,
Gzip: false,
ProfilePath: "",
Sessions: DefaultSessionsConfiguration(),
Websocket: DefaultWebsocketConfiguration(),
Tester: DefaultTesterConfiguration(),
@@ -579,9 +747,6 @@ func DefaultWebsocketConfiguration() WebsocketConfiguration {
// TesterConfiguration configuration used inside main config field 'Tester'
type TesterConfiguration struct {
// ListeningAddr is the virtual server's listening addr (host)
// Default is "iris-go.com:1993"
ListeningAddr string
// ExplicitURL If true then the url (should) be prepended manually, useful when want to test subdomains
// Default is false
ExplicitURL bool
@@ -591,13 +756,6 @@ type TesterConfiguration struct {
}
var (
// OptionTesterListeningAddr is the virtual server's listening addr (host)
// Default is "iris-go.com:1993"
OptionTesterListeningAddr = func(val string) OptionSet {
return func(c *Configuration) {
c.Tester.ListeningAddr = val
}
}
// OptionTesterExplicitURL If true then the url (should) be prepended manually, useful when want to test subdomains
// Default is false
OptionTesterExplicitURL = func(val bool) OptionSet {
@@ -615,367 +773,19 @@ var (
)
// DefaultTesterConfiguration returns the default configuration for a tester
// the ListeningAddr is used as virtual only when no running server is founded
func DefaultTesterConfiguration() TesterConfiguration {
return TesterConfiguration{ListeningAddr: "iris-go.com:1993", ExplicitURL: false, Debug: false}
}
// ServerConfiguration is the configuration which is used inside iris' server(s) for listening to
type ServerConfiguration struct {
// ListenningAddr the addr that server listens to
ListeningAddr string
CertFile string
KeyFile string
// AutoTLS enable to get certifications from the Letsencrypt
// when this configuration field is true, the CertFile & KeyFile are empty, no need to provide a key.
//
// example: https://github.com/iris-contrib/examples/blob/master/letsencyrpt/main.go
AutoTLS bool
// Mode this is for unix only
Mode os.FileMode
// MaxRequestBodySize Maximum request body size.
//
// The server rejects requests with bodies exceeding this limit.
//
// By default request body size is 8MB.
MaxRequestBodySize int
// Per-connection buffer size for requests' reading.
// This also limits the maximum header size.
//
// Increase this buffer if your clients send multi-KB RequestURIs
// and/or multi-KB headers (for example, BIG cookies).
//
// Default buffer size is used if not set.
ReadBufferSize int
// Per-connection buffer size for responses' writing.
//
// Default buffer size is used if not set.
WriteBufferSize int
// Maximum duration for reading the full request (including body).
//
// This also limits the maximum duration for idle keep-alive
// connections.
//
// By default request read timeout is unlimited.
ReadTimeout time.Duration
// Maximum duration for writing the full response (including body).
//
// By default response write timeout is unlimited.
WriteTimeout time.Duration
// Maximum number of concurrent client connections allowed per IP.
//
// By default unlimited number of concurrent connections
// may be established to the server from a single IP address.
// Usage: iris.ListenTo{iris.OptionServerListeningAddr(":8080"), iris.OptionServerMaxConnsPerIP(300)}
// or: iris.ListenTo(iris.ServerConfiguration{ListeningAddr: ":8080", MaxConnsPerIP: 300})
// for an optional second server with a different port you can always use:
// iris.AddServer(iris.ServerConfiguration{ListeningAddr: ":9090", MaxConnsPerIP: 300})
MaxConnsPerIP int
// Maximum number of requests served per connection.
//
// The server closes connection after the last request.
// 'Connection: close' header is added to the last response.
//
// By default unlimited number of requests may be served per connection.
// Usage: iris.ListenTo{iris.OptionServerListeningAddr(":8080"), iris.OptionServerMaxConnsPerIP(300)}
// or: iris.ListenTo(iris.ServerConfiguration{ListeningAddr: ":8080", MaxRequestsPerConn:100})
// for an optional second server with a different port you can always use:
// iris.AddServer(iris.ServerConfiguration{ListeningAddr: ":9090", MaxRequestsPerConn:100})
MaxRequestsPerConn int
// RedirectTo, defaults to empty, set it in order to override the station's handler and redirect all requests to this address which is of form(HOST:PORT or :PORT)
//
// NOTE: the http status is 'StatusMovedPermanently', means one-time-redirect(the browser remembers the new addr and goes to the new address without need to request something from this server
// which means that if you want to change this address you have to clear your browser's cache in order this to be able to change to the new addr.
//
// example: https://github.com/iris-contrib/examples/tree/master/multiserver_listening2
RedirectTo string
// Virtual If this server is not really listens to a real host, it mostly used in order to achieve testing without system modifications
Virtual bool
// VListeningAddr, can be used for both virtual = true or false,
// if it's setted to not empty, then the server's Host() will return this addr instead of the ListeningAddr.
// server's Host() is used inside global template helper funcs
// set it when you are sure you know what it does.
//
// Default is empty ""
VListeningAddr string
// VScheme if setted to not empty value then all template's helper funcs prepends that as the url scheme instead of the real scheme
// server's .Scheme returns VScheme if not empty && differs from real scheme
//
// Default is empty ""
VScheme string
// Name the server's name, defaults to "iris".
// You're free to change it, but I will trust you to don't, this is the only setting whose somebody, like me, can see if iris web framework is used
Name string
}
// note: ServerConfiguration is the only one config which has its own option setter because
// it's independent from a specific iris instance:
// same server can run on multi iris instance
// one iris instance/station can have and listening to more than one server.
// OptionServerSettter server configuration option setter
type OptionServerSettter interface {
Set(c *ServerConfiguration)
}
// OptionServerSet is the func which implements the OptionServerSettter, this is used widely
type OptionServerSet func(c *ServerConfiguration)
// Set is the func which makes OptionServerSet implements the OptionServerSettter
func (o OptionServerSet) Set(c *ServerConfiguration) {
o(c)
}
// Set implements the OptionServerSettter to the ServerConfiguration
func (c ServerConfiguration) Set(main *ServerConfiguration) {
mergo.MergeWithOverwrite(main, c)
}
// Options for ServerConfiguration
var (
OptionServerListeningAddr = func(val string) OptionServerSet {
return func(c *ServerConfiguration) {
c.ListeningAddr = val
}
}
OptionServerCertFile = func(val string) OptionServerSet {
return func(c *ServerConfiguration) {
c.CertFile = val
}
}
OptionServerKeyFile = func(val string) OptionServerSet {
return func(c *ServerConfiguration) {
c.KeyFile = val
}
}
// AutoTLS enable to get certifications from the Letsencrypt
// when this configuration field is true, the CertFile & KeyFile are empty, no need to provide a key.
//
// example: https://github.com/iris-contrib/examples/blob/master/letsencyrpt/main.go
OptionServerAutoTLS = func(val bool) OptionServerSet {
return func(c *ServerConfiguration) {
c.AutoTLS = val
}
}
// Mode this is for unix only
OptionServerMode = func(val os.FileMode) OptionServerSet {
return func(c *ServerConfiguration) {
c.Mode = val
}
}
// OptionServerMaxRequestBodySize Maximum request body size.
//
// The server rejects requests with bodies exceeding this limit.
//
// By default request body size is 8MB.
OptionServerMaxRequestBodySize = func(val int) OptionServerSet {
return func(c *ServerConfiguration) {
c.MaxRequestBodySize = val
}
}
// Per-connection buffer size for requests' reading.
// This also limits the maximum header size.
//
// Increase this buffer if your clients send multi-KB RequestURIs
// and/or multi-KB headers (for example, BIG cookies).
//
// Default buffer size is used if not set.
OptionServerReadBufferSize = func(val int) OptionServerSet {
return func(c *ServerConfiguration) {
c.ReadBufferSize = val
}
}
// Per-connection buffer size for responses' writing.
//
// Default buffer size is used if not set.
OptionServerWriteBufferSize = func(val int) OptionServerSet {
return func(c *ServerConfiguration) {
c.WriteBufferSize = val
}
}
// Maximum duration for reading the full request (including body).
//
// This also limits the maximum duration for idle keep-alive
// connections.
//
// By default request read timeout is unlimited.
OptionServerReadTimeout = func(val time.Duration) OptionServerSet {
return func(c *ServerConfiguration) {
c.ReadTimeout = val
}
}
// Maximum duration for writing the full response (including body).
//
// By default response write timeout is unlimited.
OptionServerWriteTimeout = func(val time.Duration) OptionServerSet {
return func(c *ServerConfiguration) {
c.WriteTimeout = val
}
}
// OptionServerMaxConnsPerIP Maximum number of concurrent client connections allowed per IP.
//
// By default unlimited number of concurrent connections
// may be established to the server from a single IP address.
OptionServerMaxConnsPerIP = func(val int) OptionServerSet {
return func(c *ServerConfiguration) {
c.MaxConnsPerIP = val
}
}
// OptionServerMaxRequestsPerConn Maximum number of requests served per connection.
//
// The server closes connection after the last request.
// 'Connection: close' header is added to the last response.
//
// By default unlimited number of requests may be served per connection.
OptionServerMaxRequestsPerConn = func(val int) OptionServerSet {
return func(c *ServerConfiguration) {
c.MaxRequestsPerConn = val
}
}
// RedirectTo, defaults to empty, set it in order to override the station's handler and redirect all requests to this address which is of form(HOST:PORT or :PORT)
//
// NOTE: the http status is 'StatusMovedPermanently', means one-time-redirect(the browser remembers the new addr and goes to the new address without need to request something from this server
// which means that if you want to change this address you have to clear your browser's cache in order this to be able to change to the new addr.
//
// example: https://github.com/iris-contrib/examples/tree/master/multiserver_listening2
OptionServerRedirectTo = func(val string) OptionServerSet {
return func(c *ServerConfiguration) {
c.RedirectTo = val
}
}
// OptionServerVirtual If this server is not really listens to a real host, it mostly used in order to achieve testing without system modifications
OptionServerVirtual = func(val bool) OptionServerSet {
return func(c *ServerConfiguration) {
c.Virtual = val
}
}
// OptionServerVListeningAddr, can be used for both virtual = true or false,
// if it's setted to not empty, then the server's Host() will return this addr instead of the ListeningAddr.
// server's Host() is used inside global template helper funcs
// set it when you are sure you know what it does.
//
// Default is empty ""
OptionServerVListeningAddr = func(val string) OptionServerSet {
return func(c *ServerConfiguration) {
c.VListeningAddr = val
}
}
// OptionServerVScheme if setted to not empty value then all template's helper funcs prepends that as the url scheme instead of the real scheme
// server's .Scheme returns VScheme if not empty && differs from real scheme
//
// Default is empty ""
OptionServerVScheme = func(val string) OptionServerSet {
return func(c *ServerConfiguration) {
c.VScheme = val
}
}
// OptionServerName the server's name, defaults to "iris".
// You're free to change it, but I will trust you to don't, this is the only setting whose somebody, like me, can see if iris web framework is used
OptionServerName = func(val string) OptionServerSet {
return func(c *ServerConfiguration) {
c.ListeningAddr = val
}
}
)
// ServerParseAddr parses the listening addr and returns this
func ServerParseAddr(listeningAddr string) string {
// check if addr has :port, if not do it +:80 ,we need the hostname for many cases
a := listeningAddr
if a == "" {
// check for os environments
if oshost := os.Getenv("HOST"); oshost != "" {
a = oshost
} else if oshost := os.Getenv("ADDR"); oshost != "" {
a = oshost
} else if osport := os.Getenv("PORT"); osport != "" {
a = ":" + osport
}
if a == "" {
a = DefaultServerAddr
}
}
if portIdx := strings.IndexByte(a, ':'); portIdx == 0 {
// if contains only :port ,then the : is the first letter, so we dont have setted a hostname, lets set it
a = DefaultServerHostname + a
}
if portIdx := strings.IndexByte(a, ':'); portIdx < 0 {
// missing port part, add it
a = a + ":80"
}
return a
return TesterConfiguration{ExplicitURL: false, Debug: false}
}
// Default values for base Server conf
const (
// DefaultServerHostname returns the default hostname which is 0.0.0.0
DefaultServerHostname = "0.0.0.0"
// DefaultServerPort returns the default port which is 8080
// DefaultServerPort returns the default port which is 8080, not used
DefaultServerPort = 8080
// DefaultMaxRequestBodySize is 8MB
DefaultMaxRequestBodySize = 2 * fasthttp.DefaultMaxRequestBodySize
// Per-connection buffer size for requests' reading.
// This also limits the maximum header size.
//
// Increase this buffer if your clients send multi-KB RequestURIs
// and/or multi-KB headers (for example, BIG cookies).
//
// Default buffer size is 8MB
DefaultReadBufferSize = 8096
// Per-connection buffer size for responses' writing.
//
// Default buffer size is 8MB
DefaultWriteBufferSize = 8096
// DefaultServerName the response header of the 'Server' value when writes to the client
DefaultServerName = "iris"
)
var (
// DefaultServerAddr the default server addr which is: 0.0.0.0:8080
DefaultServerAddr = DefaultServerHostname + ":" + strconv.Itoa(DefaultServerPort)
)
// DefaultServerConfiguration returns the default configs for the server
func DefaultServerConfiguration() ServerConfiguration {
return ServerConfiguration{
ListeningAddr: DefaultServerAddr,
Name: DefaultServerName,
MaxRequestBodySize: DefaultMaxRequestBodySize,
ReadBufferSize: DefaultReadBufferSize,
WriteBufferSize: DefaultWriteBufferSize,
MaxConnsPerIP: 0,
MaxRequestsPerConn: 0,
RedirectTo: "",
Virtual: false,
VListeningAddr: "",
VScheme: "",
}
}

View File

@@ -67,15 +67,15 @@ func TestConfigOptionsDeep(t *testing.T) {
cookiename := "MYSESSIONID"
charset := "MYCHARSET"
dev := true
profilePath := "/mypprof"
vhost := "mydomain.com"
// first session, after charset,dev and profilepath, no canonical order.
api := New(OptionSessionsCookie(cookiename), OptionCharset(charset), OptionIsDevelopment(dev), OptionProfilePath(profilePath))
api := New(OptionSessionsCookie(cookiename), OptionCharset(charset), OptionIsDevelopment(dev), OptionVHost(vhost))
expected := DefaultConfiguration()
expected.Sessions.Cookie = cookiename
expected.Charset = charset
expected.IsDevelopment = dev
expected.ProfilePath = profilePath
expected.VHost = vhost
has := *api.Config
@@ -83,20 +83,3 @@ func TestConfigOptionsDeep(t *testing.T) {
t.Fatalf("DEEP configuration is not the same after New expected:\n %#v \ngot:\n %#v", expected, has)
}
}
// ServerConfiguration is independent so make a small test for that
func TestConfigServerOptions(t *testing.T) {
expected := DefaultServerConfiguration()
expected.ListeningAddr = "mydomain.com:80"
expected.RedirectTo = "https://mydomain.com:443"
expected.Virtual = true
c := ServerConfiguration{ListeningAddr: expected.ListeningAddr, RedirectTo: expected.RedirectTo, Virtual: expected.Virtual}
// static config test
s := newServer(c)
if !reflect.DeepEqual(s.Config, expected) {
t.Fatalf("Static Server Configuration not equal after newServer, expected:\n%#v \nwhile got:\n%#v", expected, s.Config)
}
}

View File

@@ -205,7 +205,7 @@ func (ctx *Context) HostString() string {
func (ctx *Context) VirtualHostname() string {
realhost := ctx.HostString()
hostname := realhost
virtualhost := ctx.framework.Servers.Main().Hostname()
virtualhost := ctx.framework.mux.hostname
if portIdx := strings.IndexByte(hostname, ':'); portIdx > 0 {
hostname = hostname[0:portIdx]

View File

@@ -128,7 +128,7 @@ func TestContextURLParams(t *testing.T) {
// hoststring returns the full host, will return the HOST:IP
func TestContextHostString(t *testing.T) {
initDefault()
Config.Tester.ListeningAddr = "localhost:8080"
Default.Config.VHost = "0.0.0.0:8080"
Get("/", func(ctx *Context) {
ctx.Write(ctx.HostString())
})
@@ -138,8 +138,8 @@ func TestContextHostString(t *testing.T) {
})
e := Tester(t)
e.GET("/").Expect().Status(StatusOK).Body().Equal(Config.Tester.ListeningAddr)
e.GET("/wrong").Expect().Body().NotEqual(Config.Tester.ListeningAddr)
e.GET("/").Expect().Status(StatusOK).Body().Equal(Default.Config.VHost)
e.GET("/wrong").Expect().Body().NotEqual(Default.Config.VHost)
}
// VirtualHostname returns the hostname only,
@@ -147,7 +147,7 @@ func TestContextHostString(t *testing.T) {
func TestContextVirtualHostName(t *testing.T) {
initDefault()
vhost := "mycustomvirtualname.com"
Config.Tester.ListeningAddr = vhost + ":8080"
Default.Config.VHost = vhost + ":8080"
Get("/", func(ctx *Context) {
ctx.Write(ctx.VirtualHostname())
})
@@ -176,13 +176,15 @@ func TestContextFormValueString(t *testing.T) {
func TestContextSubdomain(t *testing.T) {
initDefault()
Config.Tester.ListeningAddr = "mydomain.com:9999"
//Config.Tester.ExplicitURL = true
Default.Config.VHost = "mydomain.com:9999"
//Default.Config.Tester.ListeningAddr = "mydomain.com:9999"
// Default.Config.Tester.ExplicitURL = true
Party("mysubdomain.").Get("/mypath", func(ctx *Context) {
ctx.Write(ctx.Subdomain())
})
e := Tester(t)
e.GET("/").WithURL("http://mysubdomain.mydomain.com:9999").Expect().Status(StatusNotFound)
e.GET("/mypath").WithURL("http://mysubdomain.mydomain.com:9999").Expect().Status(StatusOK).Body().Equal("mysubdomain")
@@ -560,7 +562,7 @@ func TestContextSessions(t *testing.T) {
// request cookie should be empty
Get("/after_destroy", func(ctx *Context) {
})
Default.Config.VHost = "mydomain.com"
e := Tester(t)
e.POST("/set").WithJSON(values).Expect().Status(StatusOK).Cookies().NotEmpty()

659
http.go
View File

@@ -3,23 +3,20 @@ package iris
import (
"bytes"
"crypto/tls"
"github.com/iris-contrib/letsencrypt"
"github.com/kataras/go-errors"
"github.com/kataras/iris/utils"
"github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/fasthttpadaptor"
"log"
"net"
"net/http"
"net/http/pprof"
"os"
"sort"
"strconv"
"strings"
"sync"
"time"
"log"
"github.com/iris-contrib/letsencrypt"
"github.com/kataras/go-errors"
"github.com/kataras/iris/utils"
"github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/fasthttpadaptor"
)
const (
@@ -229,370 +226,6 @@ func StatusText(code int) string {
return statusText[code]
}
// Errors introduced by server.
var (
errServerPortAlreadyUsed = errors.New("Server can't run, port is already used")
errServerAlreadyStarted = errors.New("Server is already started and listening")
errServerHandlerMissing = errors.New("Handler is missing from server, can't start without handler")
errServerIsClosed = errors.New("Can't close the server, propably is already closed or never started")
errServerRemoveUnix = errors.New("Unexpected error when trying to remove unix socket file. Addr: %s | Trace: %s")
errServerChmod = errors.New("Cannot chmod %#o for %q: %s")
)
type (
// Server the http server
Server struct {
*fasthttp.Server
listener net.Listener
Config ServerConfiguration
tls bool
mu sync.Mutex
}
// ServerList contains the servers connected to the Iris station
ServerList struct {
mux *serveMux
servers []*Server
}
)
// newServer returns a pointer to a Server object, and set it's options if any, nothing more
func newServer(setters ...OptionServerSettter) *Server {
c := DefaultServerConfiguration()
for _, setter := range setters {
setter.Set(&c)
}
s := &Server{Server: &fasthttp.Server{Name: c.Name}, Config: c}
return s
}
// IsListening returns true if server is listening/started, otherwise false
func (s *Server) IsListening() bool {
if s == nil {
return false
}
s.mu.Lock()
defer s.mu.Unlock()
return s.listener != nil && s.listener.Addr().String() != ""
}
// IsOpened checks if handler is not nil and returns true if not, otherwise false
// this is used to see if a server has opened, use IsListening if you want to see if the server is actually ready to serve connections
func (s *Server) IsOpened() bool {
if s == nil {
return false
}
return s.Server != nil && s.Server.Handler != nil
}
// IsSecure returns true if server uses TLS, otherwise false
func (s *Server) IsSecure() bool {
return s.tls || s.Config.AutoTLS // for any case
}
// Listener returns the net.Listener which this server (is) listening to
func (s *Server) Listener() net.Listener {
return s.listener
}
// Host returns the registered host for the server
func (s *Server) Host() string {
if s.Config.VListeningAddr != "" {
return s.Config.VListeningAddr
}
return s.Config.ListeningAddr
}
// Port returns the port which server listening for
// if no port given with the ListeningAddr, it returns 80
func (s *Server) Port() int {
a := s.Host()
if portIdx := strings.IndexByte(a, ':'); portIdx != -1 {
p, err := strconv.Atoi(a[portIdx+1:])
if err != nil {
if s.Config.AutoTLS {
return 443
}
return 80
}
return p
}
if s.Config.AutoTLS {
return 443
}
return 80
}
// Scheme returns http:// or https:// if SSL is enabled
func (s *Server) Scheme() string {
scheme := "http://"
// we need to be able to take that before(for testing &debugging) and after server's listen
if s.IsSecure() || (s.Config.CertFile != "" && s.Config.KeyFile != "") || s.Config.AutoTLS {
scheme = "https://"
}
// but if virtual scheme is setted and it differs from the real scheme, return the vscheme
// the developer should set it correctly, http:// or https:// or anything at the future:P
vscheme := s.Config.VScheme
if len(vscheme) > 0 && vscheme != scheme {
return vscheme
}
return scheme
}
// FullHost returns the scheme+host
func (s *Server) FullHost() string {
return s.Scheme() + s.Host()
}
// Hostname returns the hostname part of the host (host expect port)
func (s *Server) Hostname() string {
a := s.Host()
idxPort := strings.IndexByte(a, ':')
if idxPort > 0 {
// port exists, (it always exists for Config.ListeningAddr
return a[0:idxPort] // except the port
} // but for Config.VListeningAddr the developer maybe doesn't uses the host:port format
// so, if no port found, then return the Host as it is, it should be something 'mydomain.com'
return a
}
func (s *Server) listen() error {
if s.IsListening() {
return errServerAlreadyStarted
}
listener, err := net.Listen("tcp4", s.Config.ListeningAddr)
if err != nil {
return err
}
go s.serve(listener) // we don't catch underline errors, we catched all already
return nil
}
func (s *Server) listenUNIX() error {
mode := s.Config.Mode
addr := s.Config.ListeningAddr
if errOs := os.Remove(addr); errOs != nil && !os.IsNotExist(errOs) {
return errServerRemoveUnix.Format(s.Config.ListeningAddr, errOs.Error())
}
listener, err := net.Listen("unix", addr)
if err != nil {
return errServerPortAlreadyUsed
}
if err = os.Chmod(addr, mode); err != nil {
return errServerChmod.Format(mode, addr, err.Error())
}
go s.serve(listener) // we don't catch underline errors, we catched all already
return nil
}
//Serve just serves a listener, it is a blocking action, plugin.PostListen is not fired here.
func (s *Server) serve(l net.Listener) error {
s.mu.Lock()
s.listener = l
s.mu.Unlock()
if s.Config.CertFile != "" && s.Config.KeyFile != "" {
s.tls = true
return s.Server.ServeTLS(s.listener, s.Config.CertFile, s.Config.KeyFile)
} else if s.Config.AutoTLS {
var m letsencrypt.Manager
if err := m.CacheFile("letsencrypt.cache"); err != nil {
return err
}
tlsConfig := &tls.Config{GetCertificate: m.GetCertificate}
ln := tls.NewListener(l, tlsConfig)
s.tls = true
s.mu.Lock()
s.listener = ln
s.mu.Unlock()
}
return s.Server.Serve(s.listener)
}
// Open opens/starts/runs/listens (to) the server, listen tls if Cert && Key is registed, listenUNIX if Mode is registed, otherwise listen
func (s *Server) Open(h fasthttp.RequestHandler) error {
if h == nil {
return errServerHandlerMissing
}
if s.IsListening() {
return errServerAlreadyStarted
}
s.Server.MaxRequestBodySize = s.Config.MaxRequestBodySize
s.Server.ReadBufferSize = s.Config.ReadBufferSize
s.Server.WriteBufferSize = s.Config.WriteBufferSize
s.Server.ReadTimeout = s.Config.ReadTimeout
s.Server.WriteTimeout = s.Config.WriteTimeout
s.Server.MaxConnsPerIP = s.Config.MaxConnsPerIP
s.Server.MaxRequestsPerConn = s.Config.MaxRequestsPerConn
if s.Config.RedirectTo != "" {
// override the handler and redirect all requests to this addr
s.Server.Handler = func(reqCtx *fasthttp.RequestCtx) {
path := string(reqCtx.Path())
redirectTo := s.Config.RedirectTo
if path != "/" {
redirectTo += "/" + path
}
reqCtx.Redirect(redirectTo, StatusMovedPermanently)
}
} else {
s.Server.Handler = h
}
if s.Config.Mode > 0 {
return s.listenUNIX()
}
s.Config.ListeningAddr = ServerParseAddr(s.Config.ListeningAddr)
if s.Config.Virtual {
return nil
}
return s.listen()
}
// Close terminates the server
func (s *Server) Close() (err error) {
if !s.IsListening() {
return errServerIsClosed
}
err = s.listener.Close()
return
}
// -------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------
// --------------------------------ServerList implementation-----------------------------
// -------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------
// Add adds a server to the list by its config
// returns the new server
func (s *ServerList) Add(setters ...OptionServerSettter) *Server {
srv := newServer(setters...)
s.servers = append(s.servers, srv)
return srv
}
// Len returns the size of the server list
func (s *ServerList) Len() int {
return len(s.servers)
}
// Main returns the main server,
// the last added server is the main server, even if's Virtual
func (s *ServerList) Main() (srv *Server) {
l := len(s.servers) - 1
for i := range s.servers {
if i == l {
return s.servers[i]
}
}
return nil
}
// Get returns the server by it's registered Address
func (s *ServerList) Get(addr string) (srv *Server) {
for i := range s.servers {
srv = s.servers[i]
if srv.Config.ListeningAddr == addr {
return
}
}
return
}
// GetAll returns all registered servers
func (s *ServerList) GetAll() []*Server {
return s.servers
}
// GetByIndex returns a server from the list by it's index
func (s *ServerList) GetByIndex(i int) *Server {
if len(s.servers) >= i+1 {
return s.servers[i]
}
return nil
}
// Remove deletes a server by it's registered Address
// returns true if something was removed, otherwise returns false
func (s *ServerList) Remove(addr string) bool {
servers := s.servers
for i := range servers {
srv := servers[i]
if srv.Config.ListeningAddr == addr {
copy(servers[i:], servers[i+1:])
servers[len(servers)-1] = nil
s.servers = servers[:len(servers)-1]
return true
}
}
return false
}
// CloseAll terminates all listening servers
// returns the first error, if erro happens it continues to closes the rest of the servers
func (s *ServerList) CloseAll() (err error) {
for i := range s.servers {
if err == nil {
err = s.servers[i].Close()
}
}
return
}
// OpenAll starts all servers
// returns the first error happens to one of these servers
// if one server gets error it closes the previous servers and exits from this process
func (s *ServerList) OpenAll(reqHandler fasthttp.RequestHandler) error {
l := len(s.servers) - 1
for i := range s.servers {
if err := s.servers[i].Open(reqHandler); err != nil {
time.Sleep(2 * time.Second)
// for any case,
// we don't care about performance on initialization,
// we must make sure that the previous servers are running before closing them
s.CloseAll()
return err
}
if i == l {
s.mux.setHostname(s.servers[i].Hostname())
}
}
return nil
}
// GetAllOpened returns all opened/started servers
func (s *ServerList) GetAllOpened() (servers []*Server) {
for i := range s.servers {
if s.servers[i].IsOpened() {
servers = append(servers, s.servers[i])
}
}
return
}
// errHandler returns na error with message: 'Passed argument is not func(*Context) neither an object which implements the iris.Handler with Serve(ctx *Context)
// It seems to be a +type Points to: +pointer.'
var errHandler = errors.New("Passed argument is not func(*Context) neither an object which implements the iris.Handler with Serve(ctx *Context)\n It seems to be a %T Points to: %v.")
@@ -620,7 +253,7 @@ func (h HandlerFunc) Serve(ctx *Context) {
h(ctx)
}
// ToHandler converts an httapi.Handler or http.HandlerFunc to an iris.Handler
// ToHandler converts an http.Handler or http.HandlerFunc to an iris.Handler
func ToHandler(handler interface{}) Handler {
//this is not the best way to do it, but I dont have any options right now.
switch handler.(type) {
@@ -677,44 +310,6 @@ func joinMiddleware(middleware1 Middleware, middleware2 Middleware) Middleware {
return newMiddleware
}
func profileMiddleware(debugPath string) Middleware {
htmlMiddleware := HandlerFunc(func(ctx *Context) {
ctx.SetContentType(contentHTML + "; charset=" + ctx.framework.Config.Charset)
ctx.Next()
})
indexHandler := ToHandlerFunc(pprof.Index)
cmdlineHandler := ToHandlerFunc(pprof.Cmdline)
profileHandler := ToHandlerFunc(pprof.Profile)
symbolHandler := ToHandlerFunc(pprof.Symbol)
goroutineHandler := ToHandlerFunc(pprof.Handler("goroutine"))
heapHandler := ToHandlerFunc(pprof.Handler("heap"))
threadcreateHandler := ToHandlerFunc(pprof.Handler("threadcreate"))
debugBlockHandler := ToHandlerFunc(pprof.Handler("block"))
return Middleware{htmlMiddleware, HandlerFunc(func(ctx *Context) {
action := ctx.Param("action")
if len(action) > 1 {
if strings.Contains(action, "cmdline") {
cmdlineHandler.Serve((ctx))
} else if strings.Contains(action, "profile") {
profileHandler.Serve(ctx)
} else if strings.Contains(action, "symbol") {
symbolHandler.Serve(ctx)
} else if strings.Contains(action, "goroutine") {
goroutineHandler.Serve(ctx)
} else if strings.Contains(action, "heap") {
heapHandler.Serve(ctx)
} else if strings.Contains(action, "threadcreate") {
threadcreateHandler.Serve(ctx)
} else if strings.Contains(action, "debug/block") {
debugBlockHandler.Serve(ctx)
}
} else {
indexHandler.Serve(ctx)
}
})}
}
const (
// parameterStartByte is very used on the node, it's just contains the byte for the ':' rune/char
parameterStartByte = byte(':')
@@ -1539,9 +1134,11 @@ func (mux *serveMux) BuildHandler() HandlerFunc {
// context.VirtualHost() is a slow method because it makes string.Replaces but user can understand that if subdomain then server will have some nano/or/milleseconds performance cost
requestHost := context.VirtualHostname()
if requestHost != mux.hostname {
//println(requestHost + " != " + mux.hostname)
// we have a subdomain
if strings.Index(tree.subdomain, dynamicSubdomainIndicator) != -1 {
} else {
//println(requestHost + " = " + mux.hostname)
// mux.host = iris-go.com:8080, the subdomain for example is api.,
// so the host must be api.iris-go.com:8080
if tree.subdomain+mux.hostname != requestHost {
@@ -1599,3 +1196,241 @@ func (mux *serveMux) BuildHandler() HandlerFunc {
mux.fireError(StatusNotFound, context)
}
}
var (
errPortAlreadyUsed = errors.New("Port is already used")
errRemoveUnix = errors.New("Unexpected error when trying to remove unix socket file. Addr: %s | Trace: %s")
errChmod = errors.New("Cannot chmod %#o for %q: %s")
errCertKeyMissing = errors.New("You should provide certFile and keyFile for TLS/SSL")
errParseTLS = errors.New("Couldn't load TLS, certFile=%q, keyFile=%q. Trace: %s")
)
// TCP4 returns a new tcp4 Listener
// *tcp6 has some bugs in some operating systems, as reported by Go Community*
func TCP4(addr string) (net.Listener, error) {
return net.Listen("tcp4", ParseHost(addr))
}
// UNIX returns a new unix(file) Listener
func UNIX(addr string, mode os.FileMode) (net.Listener, error) {
if errOs := os.Remove(addr); errOs != nil && !os.IsNotExist(errOs) {
return nil, errRemoveUnix.Format(addr, errOs.Error())
}
listener, err := net.Listen("unix", addr)
if err != nil {
return nil, errPortAlreadyUsed.AppendErr(err)
}
if err = os.Chmod(addr, mode); err != nil {
return nil, errChmod.Format(mode, addr, err.Error())
}
return listener, nil
}
// TLS returns a new TLS Listener
func TLS(addr, certFile, keyFile string) (net.Listener, error) {
if certFile == "" || keyFile == "" {
return nil, errCertKeyMissing
}
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, errParseTLS.Format(certFile, keyFile, err)
}
return CERT(addr, cert)
}
// CERT returns a listener which contans tls.Config with the provided certificate, use for ssl
func CERT(addr string, cert tls.Certificate) (net.Listener, error) {
ln, err := TCP4(addr)
if err != nil {
return nil, err
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
PreferServerCipherSuites: true,
}
return tls.NewListener(ln, tlsConfig), nil
}
// LETSENCRYPT returns a new Automatic TLS Listener using letsencrypt.org service
func LETSENCRYPT(addr string) (net.Listener, error) {
if portIdx := strings.IndexByte(addr, ':'); portIdx == -1 {
addr += ":443"
}
ln, err := TCP4(addr)
if err != nil {
return nil, err
}
var m letsencrypt.Manager
if err = m.CacheFile("letsencrypt.cache"); err != nil {
return nil, err
}
tlsConfig := &tls.Config{GetCertificate: m.GetCertificate}
tlsLn := tls.NewListener(ln, tlsConfig)
return tlsLn, nil
}
// TCPKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections.
// Dead TCP connections (e.g. closing laptop mid-download) eventually
// go away
// It is not used by default if you want to pass a keep alive listener
// then just pass the child listener, example:
// listener := iris.TCPKeepAliveListener{iris.TCP4(":8080").(*net.TCPListener)}
type TCPKeepAliveListener struct {
*net.TCPListener
}
// Accept implements the listener and sets the keep alive period which is 2minutes
func (ln TCPKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := ln.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(2 * time.Minute)
return tc, nil
}
// ParseHost tries to convert a given string to an address which is compatible with net.Listener and server
func ParseHost(addr string) string {
// check if addr has :port, if not do it +:80 ,we need the hostname for many cases
a := addr
if a == "" {
// check for os environments
if oshost := os.Getenv("ADDR"); oshost != "" {
a = oshost
} else if oshost := os.Getenv("HOST"); oshost != "" {
a = oshost
} else if oshost := os.Getenv("HOSTNAME"); oshost != "" {
a = oshost
// check for port also here
if osport := os.Getenv("PORT"); osport != "" {
a += ":" + osport
}
} else if osport := os.Getenv("PORT"); osport != "" {
a = ":" + osport
} else {
a = DefaultServerAddr
}
}
if portIdx := strings.IndexByte(a, ':'); portIdx == 0 {
if a[portIdx:] == ":https" {
a = DefaultServerHostname + ":443"
} else {
// if contains only :port ,then the : is the first letter, so we dont have setted a hostname, lets set it
a = DefaultServerHostname + a
}
}
/* changed my mind, don't add 80, this will cause problems on unix listeners, and it's not really necessary because we take the port using parsePort
if portIdx := strings.IndexByte(a, ':'); portIdx < 0 {
// missing port part, add it
a = a + ":80"
}*/
return a
}
// ParseHostname receives an addr of form host[:port] and returns the hostname part of it
// ex: localhost:8080 will return the `localhost`, mydomain.com:8080 will return the 'mydomain'
func ParseHostname(addr string) string {
idx := strings.IndexByte(addr, ':')
if idx == 0 {
// only port, then return 0.0.0.0
return "0.0.0.0"
} else if idx > 0 {
return addr[0:idx]
}
// it's already hostname
return addr
}
// ParsePort receives an addr of form host[:port] and returns the port part of it
// ex: localhost:8080 will return the `8080`, mydomain.com will return the '80'
func ParsePort(addr string) int {
if portIdx := strings.IndexByte(addr, ':'); portIdx != -1 {
afP := addr[portIdx+1:]
p, err := strconv.Atoi(afP)
if err == nil {
return p
} else if afP == "https" { // it's not number, check if it's :https
return 443
}
}
return 80
}
const (
// SchemeHTTPS returns "https://" (full)
SchemeHTTPS = "https://"
// SchemeHTTP returns "http://" (full)
SchemeHTTP = "http://"
)
// ParseScheme returns the scheme based on the host,addr,domain
// Note: the full scheme not just http*,https* *http:// *https://
func ParseScheme(domain string) string {
// pure check
if strings.HasPrefix(domain, SchemeHTTPS) || ParsePort(domain) == 443 {
return SchemeHTTPS
}
return SchemeHTTP
}
var proxyHandler = func(proxyAddr string, redirectSchemeAndHost string) fasthttp.RequestHandler {
return func(reqCtx *fasthttp.RequestCtx) {
// override the handler and redirect all requests to this addr
redirectTo := redirectSchemeAndHost
fakehost := string(reqCtx.Request.Host())
path := string(reqCtx.Path())
if strings.Count(fakehost, ".") >= 3 { // propably a subdomain, pure check but doesn't matters don't worry
if sufIdx := strings.LastIndexByte(fakehost, '.'); sufIdx > 0 {
// check if the last part is a number instead of .com/.gr...
// if it's number then it's propably is 0.0.0.0 or 127.0.0.1... so it shouldn' use subdomain
if _, err := strconv.Atoi(fakehost[sufIdx+1:]); err != nil {
// it's not number then process the try to parse the subdomain
redirectScheme := ParseScheme(redirectSchemeAndHost)
realHost := strings.Replace(redirectSchemeAndHost, redirectScheme, "", 1)
redirectHost := strings.Replace(fakehost, fakehost, realHost, 1)
redirectTo = redirectScheme + redirectHost + path
reqCtx.Redirect(redirectTo, StatusMovedPermanently)
return
}
}
}
if path != "/" {
redirectTo += path
}
reqCtx.Redirect(redirectTo, StatusMovedPermanently)
}
}
// Proxy not really a proxy, it's just
// starts a server listening on proxyAddr but redirects all requests to the redirectToSchemeAndHost+$path
// nothing special, use it only when you want to start a secondary server which its only work is to redirect from one requested path to another
//
// returns a close function
func Proxy(proxyAddr string, redirectSchemeAndHost string) func() error {
proxyAddr = ParseHost(proxyAddr)
// override the handler and redirect all requests to this addr
h := proxyHandler(proxyAddr, redirectSchemeAndHost)
prx := New(OptionDisableBanner(true))
prx.Router = h
go prx.Listen(proxyAddr)
return prx.Close
}

View File

@@ -8,6 +8,7 @@ CONTRIBUTE & DISCUSSION ABOUT TESTS TO: https://github.com/iris-contrib/tests
import (
"fmt"
"github.com/valyala/fasthttp"
"io/ioutil"
"os"
"testing"
@@ -18,129 +19,140 @@ import (
const (
testTLSCert = `-----BEGIN CERTIFICATE-----
MIIDAzCCAeugAwIBAgIJAPDsxtKV4v3uMA0GCSqGSIb3DQEBBQUAMBgxFjAUBgNV
BAMMDTEyNy4wLjAuMTo0NDMwHhcNMTYwNjI5MTMxMjU4WhcNMjYwNjI3MTMxMjU4
WjAYMRYwFAYDVQQDDA0xMjcuMC4wLjE6NDQzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA0KtAOHKrcbLwWJXgRX7XSFyu4HHHpSty4bliv8ET4sLJpbZH
XeVX05Foex7PnrurDP6e+0H5TgqqcpQM17/ZlFcyKrJcHSCgV0ZDB3Sb8RLQSLns
8a+MOSbn1WZ7TkC7d/cWlKmasQRHQ2V/cWlGooyKNEPoGaEz8MbY0wn2spyIJwsB
dciERC6317VTXbiZdoD8QbAsT+tBvEHM2m2A7B7PQmHNehtyFNbSV5uZNodvv1uv
ZTnDa6IqpjFLb1b2HNFgwmaVPmmkLuy1l9PN+o6/DUnXKKBrfPAx4JOlqTKEQpWs
pnfacTE3sWkkmOSSFltAXfkXIJFKdS/hy5J/KQIDAQABo1AwTjAdBgNVHQ4EFgQU
zr1df/c9+NyTpmyiQO8g3a8NswYwHwYDVR0jBBgwFoAUzr1df/c9+NyTpmyiQO8g
3a8NswYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEACG5shtMSDgCd
MNjOF+YmD+PX3Wy9J9zehgaDJ1K1oDvBbQFl7EOJl8lRMWITSws22Wxwh8UXVibL
sscKBp14dR3e7DdbwCVIX/JyrJyOaCfy2nNBdf1B06jYFsIHvP3vtBAb9bPNOTBQ
QE0Ztu9kCqgsmu0//sHuBEeA3d3E7wvDhlqRSxTLcLtgC1NSgkFvBw0JvwgpkX6s
M5WpSBZwZv8qpplxhFfqNy8Uf+xrpSW0pGfkHumehkQGC6/Ry7raganS0aHhDPK9
Z1bEJ2com1bFFAQsm9yIXrRVMGGCtihB2Au0Q4jpEjUbzWYM+ItZyvRAGRM6Qex6
s/jogMeRsw==
-----END CERTIFICATE-----
MIIDATCCAemgAwIBAgIJAPdE0ZyCKwVtMA0GCSqGSIb3DQEBBQUAMBcxFTATBgNV
BAMMDG15ZG9tYWluLmNvbTAeFw0xNjA5MjQwNjU3MDVaFw0yNjA5MjIwNjU3MDVa
MBcxFTATBgNVBAMMDG15ZG9tYWluLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAM9YJOV1Bl+NwEq8ZAcVU2YONBw5zGkUFJUZkL77XT0i1V473JTf
GEpNZisDman+6n+pXarC2mR4T9PkCfmk072HaZ2LXwYe9XSgxnLJZJA1fJMzdMMC
2XveINF+/eeoyW9+8ZjQPbZdHWcxi7RomXg1AOMAG2UWMjalK5xkTHcqDuOI2LEe
mezWHnFdBJNMTi3pNdbVr7BjexZTSGhx4LAIP2ufTUoVzk+Cvyr4IhS00zOiyyWv
tuJaO20Q0Q5n34o9vDAACKAfNRLBE8qzdRwsjMumXTX3hJzvgFp/4Lr5Hr2I2fBd
tbIWN9xIsu6IibBGFItiAfQSrKAR7IFVqDUCAwEAAaNQME4wHQYDVR0OBBYEFNvN
Yik2eBRDmDaqoMaLfvr75kGfMB8GA1UdIwQYMBaAFNvNYik2eBRDmDaqoMaLfvr7
5kGfMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAEAv3pKkmDTXIChB
nVrbYwNibin9HYOPf3JCjU48V672YPgbfJM0WfTvLXNVBOdTz3aIUrhfwv/go2Jz
yDcIFdLUdwllovhj1RwI96lbgCJ4AKoO/fvJ5Rxq+/vvLYB38PNl/fVEnOOeWzCQ
qHfjckShNV5GzJPhfpYn9Gj6+Zj3O0cJXhF9L/FlbVxFhwPjPRbFDNTHYzgiHy82
zhhDhTQJVnNJXfokdlg9OjfFkySqpv9fvPi/dfk5j1KmKUiYP5SYUhZiKX1JScvx
JgesCwz1nUfVQ1TYE0dphU8mTCN4Y42i7bctx7iLcDRIFhtYstt0hhCKSxFmJSBG
y9RITRA=
-----END CERTIFICATE-----
`
testTLSKey = `-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA0KtAOHKrcbLwWJXgRX7XSFyu4HHHpSty4bliv8ET4sLJpbZH
XeVX05Foex7PnrurDP6e+0H5TgqqcpQM17/ZlFcyKrJcHSCgV0ZDB3Sb8RLQSLns
8a+MOSbn1WZ7TkC7d/cWlKmasQRHQ2V/cWlGooyKNEPoGaEz8MbY0wn2spyIJwsB
dciERC6317VTXbiZdoD8QbAsT+tBvEHM2m2A7B7PQmHNehtyFNbSV5uZNodvv1uv
ZTnDa6IqpjFLb1b2HNFgwmaVPmmkLuy1l9PN+o6/DUnXKKBrfPAx4JOlqTKEQpWs
pnfacTE3sWkkmOSSFltAXfkXIJFKdS/hy5J/KQIDAQABAoIBAQDCd+bo9I0s8Fun
4z3Y5oYSDTZ5O/CY0O5GyXPrSzCSM4Cj7EWEj1mTdb9Ohv9tam7WNHHLrcd+4NfK
4ok5hLVs1vqM6h6IksB7taKATz+Jo0PzkzrsXvMqzERhEBo4aoGMIv2rXIkrEdas
S+pCsp8+nAWtAeBMCn0Slu65d16vQxwgfod6YZfvMKbvfhOIOShl9ejQ+JxVZcMw
Ti8sgvYmFUrdrEH3nCgptARwbx4QwlHGaw/cLGHdepfFsVaNQsEzc7m61fSO70m4
NYJv48ZgjOooF5AccbEcQW9IxxikwNc+wpFYy5vDGzrBwS5zLZQFpoyMWFhtWdjx
hbmNn1jlAoGBAPs0ZjqsfDrH5ja4dQIdu5ErOccsmoHfMMBftMRqNG5neQXEmoLc
Uz8WeQ/QDf302aTua6E9iSjd7gglbFukVwMndQ1Q8Rwxz10jkXfiE32lFnqK0csx
ltruU6hOeSGSJhtGWBuNrT93G2lmy23fSG6BqOzdU4rn/2GPXy5zaxM/AoGBANSm
/E96RcBUiI6rDVqKhY+7M1yjLB41JrErL9a0Qfa6kYnaXMr84pOqVN11IjhNNTgl
g1lwxlpXZcZh7rYu9b7EEMdiWrJDQV7OxLDHopqUWkQ+3MHwqs6CxchyCq7kv9Df
IKqat7Me6Cyeo0MqcW+UMxlCRBxKQ9jqC7hDfZuXAoGBAJmyS8ImerP0TtS4M08i
JfsCOY21qqs/hbKOXCm42W+be56d1fOvHngBJf0YzRbO0sNo5Q14ew04DEWLsCq5
+EsDv0hwd7VKfJd+BakV99ruQTyk5wutwaEeJK1bph12MD6L4aiqHJAyLeFldZ45
+TUzu8mA+XaJz+U/NXtUPvU9AoGBALtl9M+tdy6I0Fa50ujJTe5eEGNAwK5WNKTI
5D2XWNIvk/Yh4shXlux+nI8UnHV1RMMX++qkAYi3oE71GsKeG55jdk3fFQInVsJQ
APGw3FDRD8M4ip62ki+u+tEr/tIlcAyHtWfjNKO7RuubWVDlZFXqCiXmSdOMdsH/
bxiREW49AoGACWev/eOzBoQJCRN6EvU2OV0s3b6f1QsPvcaH0zc6bgbBFOGmJU8v
pXhD88tsu9exptLkGVoYZjR0n0QT/2Kkyu93jVDW/80P7VCz8DKYyAJDa4CVwZxO
MlobQSunSDKx/CCJhWkbytCyh1bngAtwSAYLXavYIlJbAzx6FvtAIw4=
-----END RSA PRIVATE KEY-----
`
MIIEpQIBAAKCAQEAz1gk5XUGX43ASrxkBxVTZg40HDnMaRQUlRmQvvtdPSLVXjvc
lN8YSk1mKwOZqf7qf6ldqsLaZHhP0+QJ+aTTvYdpnYtfBh71dKDGcslkkDV8kzN0
wwLZe94g0X7956jJb37xmNA9tl0dZzGLtGiZeDUA4wAbZRYyNqUrnGRMdyoO44jY
sR6Z7NYecV0Ek0xOLek11tWvsGN7FlNIaHHgsAg/a59NShXOT4K/KvgiFLTTM6LL
Ja+24lo7bRDRDmffij28MAAIoB81EsETyrN1HCyMy6ZdNfeEnO+AWn/guvkevYjZ
8F21shY33Eiy7oiJsEYUi2IB9BKsoBHsgVWoNQIDAQABAoIBABRhi67qY+f8nQw7
nHF9zSbY+pJTtB4YFTXav3mmZ7HcvLB4neQcUdzr4sETp4UoQ5Cs60IfySvbD626
WqipZQ7aQq1zx7FoVaRTMW6TEUmDmG03v6BzpUEhwoQVQYwF8Vb+WW01+vr0CDHe
kub26S8BtsaZehfjqKfqcHD9Au8ri+Nwbu91iT4POVzBBBwYbtwXZwaYDR5PCNOI
ld+6qLapVIVKpvLHL+tA4A/n0n4l7p8TJo/qYesFRZ7J+USt4YGFDuf15nnDge/7
9Qjyqa9WmvRGytPdgtEzc8XwJu7xhcRthSmCppdY0ExHBwVceCEz8u3QbRYFqq3U
iLXUpfkCgYEA6JMlRtLIEAPkJZGBxJBSaeWVOeaAaMnLAdcu4OFjSuxr65HXsdhM
aWHopCE44NjBROAg67qgc5vNBZvhnCwyTI8nb6k/CO5QZ4AG1d2Xe/9rPV5pdaBL
gRgOJjlG0apZpPVM4I/0JU5prwS2Z71lFmEMikwmbmngYmOysqUBfbMCgYEA5Dpw
qzn1Oq+fasSUjzUa/wvBTVMpVjnNrda7Hnlx6lssnQaPFqifFYL9Zr/+5rWdsE2O
NNCsy68tacspAUa0LQinzpeSP4dyTVCvZG/xWPaYDKE6FDmzsi8xHBTTxMneeU6p
HUKJMUD3LcvBiCLunhT2xd1/+LKzVce6ms9R3ncCgYEAv9wjdDmOMSgEnblbg/xL
AHEUmZ89bzSI9Au/8GP+tWAz5zF47o2w+355nGyLr3EgfuEmR1C97KEqkOX3SA5t
sBqoPcUw6v0t9zP2b5dN0Ez0+rtX5GFH6Ecf5Qh7E5ukOCDkOpyGnAADzw3kK9Bi
BAQrhCstyQguwvvb/uOAR2ECgYEA3nYcZrqaz7ZqVL8C88hW5S4HIKEkFNlJI97A
DAdiw4ZVqUXAady5HFXPPL1+8FEtQLGIIPEazXuWb53I/WZ2r8LVFunlcylKgBRa
sjLvdMEBGqZ5H0fTYabgXrfqZ9JBmcrTyyKU6b6icTBAF7u9DbfvhpTObZN6fO2v
dcEJ0ycCgYEAxM8nGR+pa16kZmW1QV62EN0ifrU7SOJHCOGApU0jvTz8D4GO2j+H
MsoPSBrZ++UYgtGO/dK4aBV1JDdy8ZdyfE6UN+a6dQgdNdbOMT4XwWdS0zlZ/+F4
PKvbgZnLEKHvjODJ65aQmcTVUoaa11J29iAAtA3a3TcMn6C2fYpRy1s=
-----END RSA PRIVATE KEY-----
`
)
func TestServerHost(t *testing.T) {
var server1, server2, server3 Server
var expectedHost1 = "mydomain.com:1993"
var expectedHost2 = "mydomain.com:80"
var expectedHost3 = DefaultServerHostname + ":9090"
server1.Config.ListeningAddr = expectedHost1
server2.Config.ListeningAddr = "mydomain.com"
server3.Config.ListeningAddr = ":9090"
func TestParseAddr(t *testing.T) {
server1.Config.ListeningAddr = ServerParseAddr(server1.Config.ListeningAddr)
server2.Config.ListeningAddr = ServerParseAddr(server2.Config.ListeningAddr)
server3.Config.ListeningAddr = ServerParseAddr(server3.Config.ListeningAddr)
// test hosts
expectedHost1 := "mydomain.com:1993"
expectedHost2 := "mydomain.com"
expectedHost3 := DefaultServerHostname + ":9090"
expectedHost4 := "mydomain.com:443"
if server1.Host() != expectedHost1 {
t.Fatalf("Expecting server 1's host to be %s but we got %s", expectedHost1, server1.Host())
}
if server2.Host() != expectedHost2 {
t.Fatalf("Expecting server 2's host to be %s but we got %s", expectedHost2, server2.Host())
}
if server3.Host() != expectedHost3 {
t.Fatalf("Expecting server 3's host to be %s but we got %s", expectedHost3, server3.Host())
}
}
host1 := ParseHost(expectedHost1)
host2 := ParseHost(expectedHost2)
host3 := ParseHost(":9090")
host4 := ParseHost(expectedHost4)
func TestServerHostname(t *testing.T) {
var server Server
var expectedHostname = "mydomain.com"
server.Config.ListeningAddr = expectedHostname + ":1993"
server.Config.ListeningAddr = ServerParseAddr(server.Config.ListeningAddr)
if server.Hostname() != expectedHostname {
t.Fatalf("Expecting server's hostname to be %s but we got %s", expectedHostname, server.Hostname())
if host1 != expectedHost1 {
t.Fatalf("Expecting server 1's host to be %s but we got %s", expectedHost1, host1)
}
}
func TestServerFullHost(t *testing.T) {
var server1 Server
var server2 Server
server1.Config.ListeningAddr = "127.0.0.1:8080"
server1.Config.CertFile = "notempty"
server1.Config.KeyFile = "notempty"
server2.Config.ListeningAddr = "127.0.0.1:8080"
server1ExpectingFullhost := "https://" + server1.Config.ListeningAddr
server2ExpectingFullhost := "http://" + server2.Config.ListeningAddr
if server1.FullHost() != server1ExpectingFullhost {
t.Fatalf("Expecting server 1's fullhost to be %s but got %s", server1ExpectingFullhost, server1.FullHost())
if host2 != expectedHost2 {
t.Fatalf("Expecting server 2's host to be %s but we got %s", expectedHost2, host2)
}
if server2.FullHost() != server2ExpectingFullhost {
t.Fatalf("Expecting server 2's fullhost to be %s but got %s", server2ExpectingFullhost, server2.FullHost())
if host3 != expectedHost3 {
t.Fatalf("Expecting server 3's host to be %s but we got %s", expectedHost3, host3)
}
if host4 != expectedHost4 {
t.Fatalf("Expecting server 4's host to be %s but we got %s", expectedHost4, host4)
}
}
// test hostname
expectedHostname1 := "mydomain.com"
expectedHostname2 := "mydomain.com"
expectedHostname3 := DefaultServerHostname
expectedHostname4 := "mydomain.com"
func TestServerPort(t *testing.T) {
var server1, server2 Server
expectedPort1 := 8080
expectedPort2 := 80
server1.Config.ListeningAddr = "mydomain.com:8080"
server2.Config.ListeningAddr = "mydomain.com"
server1.Config.ListeningAddr = ServerParseAddr(server1.Config.ListeningAddr)
server2.Config.ListeningAddr = ServerParseAddr(server2.Config.ListeningAddr)
if server1.Port() != expectedPort1 {
t.Fatalf("Expecting server 1's port to be %d but we got %d", expectedPort1, server1.Port())
hostname1 := ParseHostname(host1)
hostname2 := ParseHostname(host2)
hostname3 := ParseHostname(host3)
hostname4 := ParseHostname(host4)
if hostname1 != expectedHostname1 {
t.Fatalf("Expecting server 1's hostname to be %s but we got %s", expectedHostname1, hostname1)
}
if server2.Port() != expectedPort2 {
t.Fatalf("Expecting server 2's port to be %d but we got %d", expectedPort2, server2.Port())
if hostname2 != expectedHostname2 {
t.Fatalf("Expecting server 2's hostname to be %s but we got %s", expectedHostname2, hostname2)
}
if hostname3 != expectedHostname3 {
t.Fatalf("Expecting server 3's hostname to be %s but we got %s", expectedHostname3, hostname3)
}
if hostname4 != expectedHostname4 {
t.Fatalf("Expecting server 4's hostname to be %s but we got %s", expectedHostname4, hostname4)
}
// test scheme, no need to test fullhost(scheme+host)
expectedScheme1 := SchemeHTTP
expectedScheme2 := SchemeHTTP
expectedScheme3 := SchemeHTTP
expectedScheme4 := SchemeHTTPS
scheme1 := ParseScheme(host1)
scheme2 := ParseScheme(host2)
scheme3 := ParseScheme(host3)
scheme4 := ParseScheme(host4)
if scheme1 != expectedScheme1 {
t.Fatalf("Expecting server 1's hostname to be %s but we got %s", expectedScheme1, scheme1)
}
if scheme2 != expectedScheme2 {
t.Fatalf("Expecting server 2's hostname to be %s but we got %s", expectedScheme2, scheme2)
}
if scheme3 != expectedScheme3 {
t.Fatalf("Expecting server 3's hostname to be %s but we got %s", expectedScheme3, scheme3)
}
if scheme4 != expectedScheme4 {
t.Fatalf("Expecting server 4's hostname to be %s but we got %s", expectedScheme4, scheme4)
}
}
// Contains the server test for multi running servers
func TestMultiRunningServers_v1(t *testing.T) {
host := "mydomain.com:443" // you have to add it to your hosts file( for windows, as 127.0.0.1 mydomain.com)
func TestMultiRunningServers_v1_PROXY(t *testing.T) {
defer Close()
host := "mydomain.com" // you have to add it to your hosts file( for windows, as 127.0.0.1 mydomain.com)
initDefault()
Config.DisableBanner = true
Default.Config.DisableBanner = true
// create the key and cert files on the fly, and delete them when this test finished
certFile, ferr := ioutil.TempFile("", "cert")
@@ -153,9 +165,6 @@ func TestMultiRunningServers_v1(t *testing.T) {
t.Fatal(ferr.Error())
}
certFile.WriteString(testTLSCert)
keyFile.WriteString(testTLSKey)
defer func() {
certFile.Close()
time.Sleep(350 * time.Millisecond)
@@ -166,33 +175,37 @@ func TestMultiRunningServers_v1(t *testing.T) {
os.Remove(keyFile.Name())
}()
certFile.WriteString(testTLSCert)
keyFile.WriteString(testTLSKey)
Get("/", func(ctx *Context) {
ctx.Write("Hello from %s", ctx.HostString())
})
// start the secondary server
AddServer(ServerConfiguration{ListeningAddr: "mydomain.com:80", RedirectTo: "https://" + host, Virtual: true})
// start the main server
go ListenTo(ServerConfiguration{ListeningAddr: host, CertFile: certFile.Name(), KeyFile: keyFile.Name(), Virtual: true})
// prepare test framework
if ok := <-Available; !ok {
go ListenTLS(host+":443", certFile.Name(), keyFile.Name())
if ok := <-Default.Available; !ok {
t.Fatal("Unexpected error: server cannot start, please report this as bug!!")
}
closeProxy := Proxy("mydomain.com:80", "https://"+host)
defer closeProxy()
Default.Config.Tester.ExplicitURL = true
e := Tester(t)
e.Request("GET", "http://mydomain.com:80").Expect().Status(StatusOK).Body().Equal("Hello from " + host)
e.Request("GET", "http://"+host).Expect().Status(StatusOK).Body().Equal("Hello from " + host)
e.Request("GET", "https://"+host).Expect().Status(StatusOK).Body().Equal("Hello from " + host)
}
// Contains the server test for multi running servers
func TestMultiRunningServers_v2(t *testing.T) {
defer Close()
domain := "mydomain.com"
host := domain + ":443"
initDefault()
Config.DisableBanner = true
Config.Tester.ListeningAddr = host
Default.Config.DisableBanner = true
// create the key and cert files on the fly, and delete them when this test finished
certFile, ferr := ioutil.TempFile("", "cert")
@@ -219,25 +232,35 @@ func TestMultiRunningServers_v2(t *testing.T) {
}()
Get("/", func(ctx *Context) {
ctx.Write("Hello from %s", ctx.HostString())
ctx.Write("Hello from %s", string(ctx.HostString()))
})
// add a secondary server
Servers.Add(ServerConfiguration{ListeningAddr: domain + ":80", RedirectTo: "https://" + host, Virtual: true})
//Servers.Add(ServerConfiguration{ListeningAddr: domain + ":80", RedirectTo: "https://" + host, Virtual: true})
// add our primary/main server
Servers.Add(ServerConfiguration{ListeningAddr: host, CertFile: certFile.Name(), KeyFile: keyFile.Name(), Virtual: true})
//Servers.Add(ServerConfiguration{ListeningAddr: host, CertFile: certFile.Name(), KeyFile: keyFile.Name(), Virtual: true})
go Go()
//go Go()
// using the proxy handler
fsrv1 := &fasthttp.Server{Handler: proxyHandler(domain+":80", "https://"+domain)}
go fsrv1.ListenAndServe(domain + ":80")
// using the same iris' handler but not as proxy, just the same handler
fsrv2 := &fasthttp.Server{Handler: Default.Router}
go fsrv2.ListenAndServe("myotherdomain.com" + ":8080")
go ListenTLS(domain+":443", certFile.Name(), keyFile.Name())
// prepare test framework
if ok := <-Available; !ok {
t.Fatal("Unexpected error: server cannot start, please report this as bug!!")
}
Default.Config.Tester.ExplicitURL = true
e := Tester(t)
e.Request("GET", "http://"+domain+":80").Expect().Status(StatusOK).Body().Equal("Hello from " + host)
e.Request("GET", "https://"+host).Expect().Status(StatusOK).Body().Equal("Hello from " + host)
e.Request("GET", "http://"+domain).Expect().Status(StatusOK).Body().Equal("Hello from " + domain)
e.Request("GET", "http://myotherdomain.com:8080").Expect().Status(StatusOK).Body().Equal("Hello from myotherdomain.com:8080")
e.Request("GET", "https://"+domain).Expect().Status(StatusOK).Body().Equal("Hello from " + domain)
}
@@ -247,18 +270,13 @@ const (
)
func testSubdomainHost() string {
s := testSubdomain + "." + Servers.Main().Host()
s := testSubdomain + "." + Default.Config.VHost
return s
}
func testSubdomainURL() (subdomainURL string) {
func testSubdomainURL() string {
subdomainHost := testSubdomainHost()
if Servers.Main().IsSecure() {
subdomainURL = "https://" + subdomainHost
} else {
subdomainURL = "http://" + subdomainHost
}
return
return Default.Config.VScheme + subdomainHost
}
func subdomainTester(e *httpexpect.Expect) *httpexpect.Expect {
@@ -316,7 +334,7 @@ func TestMuxSimple(t *testing.T) {
{"GET", "/test_get_urlparameter2/second", "/test_get_urlparameter2/second", "name=irisurl&something=anything", "name=irisurl,something=anything", 200, true, nil, []param{{"name", "irisurl"}, {"something", "anything"}}},
{"GET", "/test_get_urlparameter2/first/second/third", "/test_get_urlparameter2/first/second/third", "name=irisurl&something=anything&else=elsehere", "name=irisurl,something=anything,else=elsehere", 200, true, nil, []param{{"name", "irisurl"}, {"something", "anything"}, {"else", "elsehere"}}},
}
defer Close()
initDefault()
for idx := range testRoutes {
@@ -360,7 +378,6 @@ func TestMuxSimple(t *testing.T) {
}
func TestMuxSimpleParty(t *testing.T) {
initDefault()
h := func(c *Context) { c.WriteString(c.HostString() + c.PathString()) }
@@ -386,12 +403,13 @@ func TestMuxSimpleParty(t *testing.T) {
p.Get("/namedpath/:param1/something/:param2/else", h)
}
Default.Config.VHost = "0.0.0.0:8080"
e := Tester(t)
request := func(reqPath string) {
e.Request("GET", reqPath).
Expect().
Status(StatusOK).Body().Equal(Servers.Main().Host() + reqPath)
Status(StatusOK).Body().Equal(Default.Config.VHost + reqPath)
}
// run the tests

757
iris.go
View File

@@ -13,7 +13,7 @@
// iris.Get("/hi_json", func(c *iris.Context) {
// c.JSON(200, iris.Map{
// "Name": "Iris",
// "Age": 2,
// "Released": "13 March 2016",
// })
// })
// iris.Listen(":8080")
@@ -30,7 +30,7 @@
// s1.Get("/hi_json", func(c *iris.Context) {
// c.JSON(200, iris.Map{
// "Name": "Iris",
// "Age": 2,
// "Released": "13 March 2016",
// })
// })
//
@@ -46,24 +46,12 @@
// -----------------------------DOCUMENTATION----------------------------
// ----------------------------_______________---------------------------
// For middleware, template engines, response engines, sessions, websockets, mails, subdomains,
// dynamic subdomains, routes, party of subdomains & routes and much more
// dynamic subdomains, routes, party of subdomains & routes, ssh and much more
// visit https://www.gitbook.com/book/kataras/iris/details
package iris // import "github.com/kataras/iris"
import (
"fmt"
"log"
"net/http"
"net/url"
"os"
"path"
"reflect"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/gavv/httpexpect"
"github.com/kataras/go-errors"
"github.com/kataras/go-fs"
@@ -71,14 +59,27 @@ import (
"github.com/kataras/go-sessions"
"github.com/kataras/go-template"
"github.com/kataras/go-template/html"
"github.com/kataras/iris/context"
"github.com/kataras/iris/utils"
"github.com/valyala/fasthttp"
"log"
"net"
"net/http"
"net/url"
"os"
"os/signal"
"path"
"reflect"
"strconv"
"strings"
"sync"
"syscall"
"testing"
"time"
)
const (
// Version is the current version of the Iris web framework
Version = "4.3.0"
Version = "4.4.0"
banner = ` _____ _
|_ _| (_)
@@ -88,26 +89,22 @@ const (
|_____|_| |_||___/ ` + Version + ` `
)
// Default entry, use it with iris.$anyPublicFunc
// Default iris instance entry and its public fields, use it with iris.$anyPublicFuncOrField
var (
Default *Framework
Config *Configuration
Logger *log.Logger // if you want colors in your console then you should use this https://github.com/iris-contrib/logger instead.
Plugins PluginContainer
Router fasthttp.RequestHandler
Websocket *WebsocketServer
// Look ssh.go for this field's configuration
// example: https://github.com/iris-contrib/examples/blob/master/ssh/main.go
SSH *SSHServer
Servers *ServerList
// Available is a channel type of bool, fired to true when the server is opened and all plugins ran
// never fires false, if the .Close called then the channel is re-allocating.
// the channel is closed only when .ListenVirtual is used, otherwise it remains open until you close it.
// the channel remains open until you close it.
//
// Note: it is a simple channel and decided to put it here and no inside HTTPServer, doesn't have statuses just true and false, simple as possible
// Where to use that?
// this is used on extreme cases when you don't know which .Listen/.ListenVirtual will be called
// and you want to run/declare something external-not-Iris (all Iris functionality declared before .Listen/.ListenVirtual) AFTER the server is started and plugins finished.
// see the server_test.go for an example
// look at the http_test.go file for a usage example
Available chan bool
)
@@ -116,9 +113,9 @@ func initDefault() {
Config = Default.Config
Logger = Default.Logger
Plugins = Default.Plugins
Router = Default.Router
Websocket = Default.Websocket
SSH = Default.SSH
Servers = Default.Servers
Available = Default.Available
}
@@ -137,18 +134,18 @@ type (
FrameworkAPI interface {
MuxAPI
Set(...OptionSetter)
CheckForUpdates(bool)
Must(error)
AddServer(...OptionServerSettter) *Server
ListenTo(...OptionServerSettter) error
Build()
Serve(net.Listener) error
Listen(string)
ListenTLS(string, string, string)
ListenLETSENCRYPT(string)
ListenUNIX(string, os.FileMode)
ListenVirtual(...string) *Server
Close() error
Reserve() error
AcquireCtx(*fasthttp.RequestCtx) *Context
ReleaseCtx(*Context)
Go() error
Close() error
CheckForUpdates(bool)
UseSessionDB(sessions.Database)
UseSerializer(string, serializer.Serializer)
UseTemplate(template.Engine) *template.Loader
@@ -169,25 +166,30 @@ type (
// Implements the FrameworkAPI
Framework struct {
*muxAPI
// Handler field which can change the default iris' mux behavior
// HTTP Server runtime fields is the iris' defined main server, developer can use unlimited number of servers
// note: they're available after .Build, and .Serve/Listen/ListenTLS/ListenLETSENCRYPT/ListenUNIX
ln net.Listener
fsrv *fasthttp.Server
Available chan bool
//
// Router field which can change the default iris' mux behavior
// if you want to get benefit with iris' context make use of:
// ctx:= iris.AcquireCtx(*fasthttp.RequestCtx) to get the context at the beginning of your handler
// iris.ReleaseCtx(ctx) to release/put the context to the pool, at the very end of your custom handler.
Handler fasthttp.RequestHandler
Router fasthttp.RequestHandler
contextPool sync.Pool
once sync.Once
Config *Configuration
sessions sessions.Sessions
serializers serializer.Serializers
templates *templateEngines
// fields which are useful to the user/dev
// the last added server is the main server
Servers *ServerList
// configuration by instance.Logger.Config
Logger *log.Logger
Plugins PluginContainer
Websocket *WebsocketServer
SSH *SSHServer
Available chan bool
// this is setted once when .Tester(t) is called
testFramework *httpexpect.Expect
}
@@ -233,7 +235,7 @@ func New(setters ...OptionSetter) *Framework {
s.sessions = sessions.New(sessions.DisableAutoGC(true))
}
// routing & http server
// routing
{
// set the servemux, which will provide us the public API also, with its context pool
mux := newServeMux(s.Logger)
@@ -243,14 +245,53 @@ func New(setters ...OptionSetter) *Framework {
}
// set the public router API (and party)
s.muxAPI = &muxAPI{mux: mux, relativePath: "/"}
s.Servers = &ServerList{mux: mux, servers: make([]*Server, 0)}
s.Available = make(chan bool)
}
return s
}
func (s *Framework) initialize() {
// Set sets an option aka configuration field to the default iris instance
func Set(setters ...OptionSetter) {
Default.Set(setters...)
}
// Set sets an option aka configuration field to this iris instance
func (s *Framework) Set(setters ...OptionSetter) {
if s.Config == nil {
defaultConfiguration := DefaultConfiguration()
s.Config = &defaultConfiguration
}
for _, setter := range setters {
setter.Set(s.Config)
}
}
// Must panics on error, it panics on registed iris' logger
func Must(err error) {
Default.Must(err)
}
// Must panics on error, it panics on registed iris' logger
func (s *Framework) Must(err error) {
if err != nil {
s.Logger.Panic(err.Error())
}
}
// Build builds the whole framework's parts together
// DO NOT CALL IT MANUALLY IF YOU ARE NOT:
// SERVE IRIS BEHIND AN EXTERNAL CUSTOM fasthttp.Server, CAN BE CALLED ONCE PER IRIS INSTANCE FOR YOUR SAFETY
func Build() {
Default.Build()
}
// Build builds the whole framework's parts together
// DO NOT CALL IT MANUALLY IF YOU ARE NOT:
// SERVE IRIS BEHIND AN EXTERNAL CUSTOM fasthttp.Server, CAN BE CALLED ONCE PER IRIS INSTANCE FOR YOUR SAFETY
func (s *Framework) Build() {
s.once.Do(func() {
// prepare the serializers, if not any other serializers setted for the default serializer types(json,jsonp,xml,markdown,text,data) then the defaults are setted:
serializer.RegisterDefaults(s.serializers)
@@ -279,14 +320,60 @@ func (s *Framework) initialize() {
s.Websocket.RegisterTo(s, s.Config.Websocket)
}
// prepare the mux & the server
// prepare the server's handler, we do that check because iris supports
// custom routers (you can take the routes registed by iris using iris.Lookups function)
if s.Router == nil {
// build and get the default mux' handler(*Context)
serve := s.mux.BuildHandler()
// build the fasthttp handler to bind it to the servers
defaultHandler := func(reqCtx *fasthttp.RequestCtx) {
ctx := s.AcquireCtx(reqCtx)
serve(ctx)
s.ReleaseCtx(ctx)
}
s.Router = defaultHandler
}
// .Build, normally*, auto-called after station's listener setted but before the real Serve, so here set the host, scheme
// and the mux hostname(*this is here because user may not call .Serve/.Listen functions if listen by a custom server)
if s.Config.VHost == "" { // if not setted by Listen functions
if s.ln != nil { // but user called .Serve
// then take the listener's addr
s.Config.VHost = s.ln.Addr().String()
} else {
// if no .Serve or .Listen called, then the user should set the VHost manually,
// however set it to a default value here for any case
s.Config.VHost = DefaultServerAddr
}
}
// if user didn't specified a scheme then get it from the VHost, which is already setted at before statements
if s.Config.VScheme == "" {
s.Config.VScheme = ParseScheme(s.Config.VHost)
}
// prepare the mux runtime fields
s.mux.setCorrectPath(!s.Config.DisablePathCorrection)
s.mux.setEscapePath(!s.Config.DisablePathEscape)
// set the mux' hostname (for multi subdomain routing)
s.mux.hostname = ParseHostname(s.Config.VHost)
// set the debug profiling handlers if ProfilePath is setted
if debugPath := s.Config.ProfilePath; debugPath != "" {
s.Handle(MethodGet, debugPath+"/*action", profileMiddleware(debugPath)...)
if s.ln != nil { // user called Listen functions or Serve,
// create the main server
s.fsrv = &fasthttp.Server{Name: DefaultServerName,
MaxRequestBodySize: s.Config.MaxRequestBodySize,
ReadBufferSize: s.Config.ReadBufferSize,
WriteBufferSize: s.Config.WriteBufferSize,
ReadTimeout: s.Config.ReadTimeout,
WriteTimeout: s.Config.WriteTimeout,
MaxConnsPerIP: s.Config.MaxConnsPerIP,
MaxRequestsPerConn: s.Config.MaxRequestsPerConn,
Handler: s.Router,
}
}
//
// ssh
if s.SSH != nil && s.SSH.Enabled() {
@@ -301,75 +388,48 @@ func (s *Framework) initialize() {
} else if s.Config.CheckForUpdates {
go s.CheckForUpdates(false)
}
})
}
// AcquireCtx gets an Iris' Context from pool
// see iris.Handler & ReleaseCtx, Go()
func AcquireCtx(reqCtx *fasthttp.RequestCtx) *Context {
return Default.AcquireCtx(reqCtx)
var (
errServerAlreadyStarted = errors.New("Server is already started and listening")
)
// Serve serves incoming connections from the given listener.
//
// Serve blocks until the given listener returns permanent error.
func Serve(ln net.Listener) error {
return Default.Serve(ln)
}
// ReleaseCtx puts the Iris' Context back to the pool in order to be re-used
// see iris.Handler & AcquireCtx, Go()
func ReleaseCtx(ctx *Context) {
Default.ReleaseCtx(ctx)
}
// Serve serves incoming connections from the given listener.
//
// Serve blocks until the given listener returns permanent error.
func (s *Framework) Serve(ln net.Listener) error {
if s.IsRunning() {
return errServerAlreadyStarted
}
// maybe a 'race' here but user should not call .Serve more than one time especially in more than one go routines...
s.ln = ln
// AcquireCtx gets an Iris' Context from pool
// see iris.Handler & ReleaseCtx, Go()
func (s *Framework) AcquireCtx(reqCtx *fasthttp.RequestCtx) *Context {
ctx := s.contextPool.Get().(*Context) // Changed to use the pool's New 09/07/2016, ~ -4k nanoseconds(9 bench tests) per requests (better performance)
ctx.RequestCtx = reqCtx
return ctx
}
// ReleaseCtx puts the Iris' Context back to the pool in order to be re-used
// see iris.Handler & AcquireCtx, Go()
func (s *Framework) ReleaseCtx(ctx *Context) {
ctx.Params = ctx.Params[0:0]
ctx.middleware = nil
ctx.session = nil
s.contextPool.Put(ctx)
}
// Go starts the iris station, listens to all registered servers, and prepare only if Virtual
func Go() error {
return Default.Go()
}
// Go starts the iris station, listens to all registered servers, and prepare only if Virtual
func (s *Framework) Go() error {
s.initialize()
s.Build()
s.Plugins.DoPreListen(s)
if s.Handler == nil { // use the 'h' which is the default mux' handler
// build and get the default mux' handler(*Context)
serve := s.mux.BuildHandler()
// build the fasthttp handler to bind it to the servers
defaultHandler := func(reqCtx *fasthttp.RequestCtx) {
ctx := s.AcquireCtx(reqCtx)
serve(ctx)
s.ReleaseCtx(ctx)
}
// This didn't helped me ,here, but maybe can help you:
// https://www.oreilly.com/learning/run-strikingly-fast-parallel-file-searches-in-go-with-sync-errgroup?utm_source=golangweekly&utm_medium=email
// new experimental package: errgroup
s.Handler = defaultHandler
defer func() {
if err := recover(); err != nil {
s.Logger.Panic(err)
}
}()
if firstErr := s.Servers.OpenAll(s.Handler); firstErr != nil {
return firstErr
}
// start the server in goroutine, .Available will block instead
go func() { s.Must(s.fsrv.Serve(ln)) }()
// print the banner
if !s.Config.DisableBanner {
openedServers := s.Servers.GetAllOpened()
l := len(openedServers)
hosts := make([]string, l, l)
for i, srv := range openedServers {
hosts[i] = srv.Host()
}
bannerMessage := fmt.Sprintf("%s: Running at %s", time.Now().Format(s.Config.TimeFormat), strings.Join(hosts, ", "))
bannerMessage := fmt.Sprintf("%s: Running at %s", time.Now().Format(s.Config.TimeFormat), s.Config.VHost)
// we don't print it via Logger because:
// 1. The banner is only 'useful' when the developer logs to terminal and no file
// 2. Prefix & LstdFlags options of the default s.Logger
@@ -380,28 +440,228 @@ func (s *Framework) Go() error {
s.Plugins.DoPostListen(s)
go func() { s.Available <- true }()
ch := make(chan os.Signal)
ch := make(chan os.Signal, 2)
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
<-ch
s.Close() // btw, don't panic here
if err := s.Close(); err != nil {
return err
}
os.Exit(1)
return nil
}
// Listen starts the standalone http server
// which listens to the addr parameter which as the form of
// host:port
//
// It panics on error if you need a func to return an error, use the Serve
func Listen(addr string) {
Default.Listen(addr)
}
// Listen starts the standalone http server
// which listens to the addr parameter which as the form of
// host:port
//
// It panics on error if you need a func to return an error, use the Serve
func (s *Framework) Listen(addr string) {
addr = ParseHost(addr)
if s.Config.VHost == "" {
s.Config.VHost = addr
// this will be set as the front-end listening addr
}
ln, err := TCP4(addr)
if err != nil {
s.Logger.Panic(err)
}
s.Must(s.Serve(ln))
}
// ListenTLS Starts a https server with 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
//
// It panics on error if you need a func to return an error, use the Serve
// ex: iris.ListenTLS(":8080","yourfile.cert","yourfile.key")
func ListenTLS(addr string, certFile string, keyFile string) {
Default.ListenTLS(addr, certFile, keyFile)
}
// ListenTLS Starts a https server with 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
//
// It panics on error if you need a func to return an error, use the Serve
// ex: iris.ListenTLS(":8080","yourfile.cert","yourfile.key")
func (s *Framework) ListenTLS(addr string, certFile, keyFile string) {
addr = ParseHost(addr)
if s.Config.VHost == "" {
s.Config.VHost = addr
// this will be set as the front-end listening addr
}
ln, err := TLS(addr, certFile, keyFile)
if err != nil {
s.Logger.Panic(err)
}
s.Must(s.Serve(ln))
}
// ListenLETSENCRYPT starts a 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_HOSTNAME:80' to the' https://$ADDR'
// example: https://github.com/iris-contrib/examples/blob/master/letsencyrpt/main.go
func ListenLETSENCRYPT(addr string) {
Default.ListenLETSENCRYPT(addr)
}
// ListenLETSENCRYPT starts a 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_HOSTNAME:80' to the' https://$ADDR'
// example: https://github.com/iris-contrib/examples/blob/master/letsencyrpt/main.go
func (s *Framework) ListenLETSENCRYPT(addr string) {
addr = ParseHost(addr)
if s.Config.VHost == "" {
s.Config.VHost = addr
// this will be set as the front-end listening addr
}
ln, err := LETSENCRYPT(addr)
if err != nil {
s.Logger.Panic(err)
}
// starts a second server which listening on :80 to redirect all requests to the :443 (https://)
Proxy(":80", "https://"+addr)
s.Must(s.Serve(ln))
}
// ListenUNIX starts the process of listening to the new requests using a 'socket file', this works only on unix
//
// It panics on error if you need a func to return an error, use the Serve
// ex: iris.ListenUNIX(":8080", Mode: os.FileMode)
func ListenUNIX(addr string, mode os.FileMode) {
Default.ListenUNIX(addr, mode)
}
// ListenUNIX starts the process of listening to the new requests using a 'socket file', this works only on unix
//
// It panics on error if you need a func to return an error, use the Serve
// ex: iris.ListenUNIX(":8080", Mode: os.FileMode)
func (s *Framework) ListenUNIX(addr string, mode os.FileMode) {
// *on unix listen we don't parse the host, because sometimes it causes problems to the user
if s.Config.VHost == "" {
s.Config.VHost = addr
// this will be set as the front-end listening addr
}
ln, err := UNIX(addr, mode)
if err != nil {
s.Logger.Panic(err)
}
s.Must(s.Serve(ln))
}
// IsRunning returns true if server is running
func IsRunning() bool {
return Default.IsRunning()
}
// IsRunning returns true if server is running
func (s *Framework) IsRunning() bool {
return s != nil && s.ln != nil && s.ln.Addr() != nil && s.ln.Addr().String() != ""
}
// DisableKeepalive whether to disable keep-alive connections.
//
// The server will close all the incoming connections after sending
// the first response to client if this option is set to true.
//
// By default keep-alive connections are enabled
//
// Note: Used on packages like graceful, after the server runs.
func DisableKeepalive(val bool) {
Default.DisableKeepalive(val)
}
// DisableKeepalive whether to disable keep-alive connections.
//
// The server will close all the incoming connections after sending
// the first response to client if this option is set to true.
//
// By default keep-alive connections are enabled
//
// Note: Used on packages like graceful, after the server runs.
func (s *Framework) DisableKeepalive(val bool) {
if s.fsrv != nil {
s.fsrv.DisableKeepalive = val
}
}
// Close terminates all the registered servers and returns an error if any
// if you want to panic on this error use the iris.Must(iris.Close())
func Close() error {
return Default.Close()
}
// Close terminates all the registered servers and returns an error if any
// if you want to panic on this error use the iris.Must(iris.Close())
func (s *Framework) Close() error {
if s.IsRunning() {
s.Plugins.DoPreClose(s)
s.Available = make(chan bool)
return s.ln.Close()
}
return nil
}
// Set sets an option aka configuration field to the default iris instance
func Set(setters ...OptionSetter) {
Default.Set(setters...)
// Reserve re-starts the server using the last .Serve's listener
func Reserve() error {
return Default.Reserve()
}
// Set sets an option aka configuration field to this iris instance
func (s *Framework) Set(setters ...OptionSetter) {
if s.Config == nil {
defaultConfiguration := DefaultConfiguration()
s.Config = &defaultConfiguration
}
// Reserve re-starts the server using the last .Serve's listener
func (s *Framework) Reserve() error {
return s.Serve(s.ln)
}
for _, setter := range setters {
setter.Set(s.Config)
}
// AcquireCtx gets an Iris' Context from pool
// see .ReleaseCtx & .Serve
func AcquireCtx(reqCtx *fasthttp.RequestCtx) *Context {
return Default.AcquireCtx(reqCtx)
}
// AcquireCtx gets an Iris' Context from pool
// see .ReleaseCtx & .Serve
func (s *Framework) AcquireCtx(reqCtx *fasthttp.RequestCtx) *Context {
ctx := s.contextPool.Get().(*Context) // Changed to use the pool's New 09/07/2016, ~ -4k nanoseconds(9 bench tests) per requests (better performance)
ctx.RequestCtx = reqCtx
return ctx
}
// ReleaseCtx puts the Iris' Context back to the pool in order to be re-used
// see .AcquireCtx & .Serve
func ReleaseCtx(ctx *Context) {
Default.ReleaseCtx(ctx)
}
// ReleaseCtx puts the Iris' Context back to the pool in order to be re-used
// see .AcquireCtx & .Serve
func (s *Framework) ReleaseCtx(ctx *Context) {
ctx.Params = ctx.Params[0:0]
ctx.middleware = nil
ctx.session = nil
s.contextPool.Put(ctx)
}
// global once because is not necessary to check for updates on more than one iris station*
@@ -454,224 +714,11 @@ func (s *Framework) CheckForUpdates(force bool) {
if s.Logger != nil {
s.Logger.Println("exiting now...")
}
os.Exit(0)
os.Exit(1)
}
}
// Must panics on error, it panics on registed iris' logger
func Must(err error) {
Default.Must(err)
}
// Must panics on error, it panics on registed iris' logger
func (s *Framework) Must(err error) {
if err != nil {
s.Logger.Panic(err.Error())
}
}
// AddServer same as .Servers.Add(ServerConfiguration)
//
// AddServer starts a server which listens to this station
// Note that the view engine's functions {{ url }} and {{ urlpath }} will return the first's registered server's scheme (http/https)
//
// this is useful mostly when you want to have two or more listening ports ( two or more servers ) for the same station
//
// receives one parameter which is the ServerConfiguration for the new server
// returns the new standalone server( you can close this server by the returning reference)
//
// If you need only one server you can use the blocking-funcs: .Listen/ListenTLS/ListenUNIX/ListenTo
//
// this is a NOT A BLOCKING version, the main .Listen/ListenTLS/ListenUNIX/ListenTo should be always executed LAST, so this function goes before the main .Listen/ListenTLS/ListenUNIX/ListenTo
func AddServer(setters ...OptionServerSettter) *Server {
return Default.AddServer(setters...)
}
// AddServer same as .Servers.Add(ServerConfiguration)
//
// AddServer starts a server which listens to this station
// Note that the view engine's functions {{ url }} and {{ urlpath }} will return the last registered server's scheme (http/https)
//
// this is useful mostly when you want to have two or more listening ports ( two or more servers ) for the same station
//
// receives one parameter which is the ServerConfiguration for the new server
// returns the new standalone server( you can close this server by the returning reference)
//
// If you need only one server you can use the blocking-funcs: .Listen/ListenTLS/ListenUNIX/ListenTo
//
// this is a NOT A BLOCKING version, the main .Listen/ListenTLS/ListenUNIX/ListenTo should be always executed LAST, so this function goes before the main .Listen/ListenTLS/ListenUNIX/ListenTo
func (s *Framework) AddServer(setters ...OptionServerSettter) *Server {
return s.Servers.Add(setters...)
}
// ListenTo listens to a server but accepts the full server's configuration
// returns an error, you're responsible to handle that
// ex: ris.ListenTo(iris.ServerConfiguration{ListeningAddr:":8080"})
// ex2: err := iris.ListenTo(iris.OptionServerListeningAddr(":8080"))
// or use the iris.Must(iris.ListenTo(iris.ServerConfiguration{ListeningAddr:":8080"}))
//
// it's a blocking func
func ListenTo(setters ...OptionServerSettter) error {
return Default.ListenTo(setters...)
}
// ListenTo listens to a server but acceots the full server's configuration
// returns an error, you're responsible to handle that
// ex: ris.ListenTo(iris.ServerConfiguration{ListeningAddr:":8080"})
// ex2: err := iris.ListenTo(iris.OptionServerListeningAddr(":8080"))
// or use the iris.Must(iris.ListenTo(iris.ServerConfiguration{ListeningAddr:":8080"}))
//
// it's a blocking func
func (s *Framework) ListenTo(setters ...OptionServerSettter) (err error) {
s.Servers.Add(setters...)
return s.Go()
}
// Listen starts the standalone http server
// which listens to the addr parameter which as the form of
// host:port
//
// It panics on error if you need a func to return an error, use the ListenTo
func Listen(addr string) {
Default.Listen(addr)
}
// Listen starts the standalone http server
// which listens to the addr parameter which as the form of
// host:port
//
// It panics on error if you need a func to return an error, use the ListenTo
func (s *Framework) Listen(addr string) {
s.Must(s.ListenTo(ServerConfiguration{ListeningAddr: addr}))
}
// ListenTLS Starts a https server with 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
//
// It panics on error if you need a func to return an error, use the ListenTo
// ex: iris.ListenTLS(":8080","yourfile.cert","yourfile.key")
func ListenTLS(addr string, certFile string, keyFile string) {
Default.ListenTLS(addr, certFile, keyFile)
}
// ListenTLSAuto starts a server listening at the specific nat address
// using key & certification taken from the letsencrypt.org 's servers
// it also starts a second 'http' server to redirect all 'http://$ADDR_HOSTNAME:80' to the' https://$ADDR'
//
// Notes:
// if you don't want the last feature you should use this method:
// iris.ListenTo(iris.ServerConfiguration{ListeningAddr: "mydomain.com:443", AutoTLS: true})
// it's a blocking function
// Limit : https://github.com/iris-contrib/letsencrypt/blob/master/lets.go#L142
//
// example: https://github.com/iris-contrib/examples/blob/master/letsencyrpt/main.go
func ListenTLSAuto(addr string) {
Default.ListenTLSAuto(addr)
}
// ListenTLS Starts a https server with 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
//
// It panics on error if you need a func to return an error, use the ListenTo
// ex: iris.ListenTLS(":8080","yourfile.cert","yourfile.key")
func (s *Framework) ListenTLS(addr string, certFile, keyFile string) {
if certFile == "" || keyFile == "" {
s.Logger.Panic("You should provide certFile and keyFile for TLS/SSL")
}
s.Must(s.ListenTo(ServerConfiguration{ListeningAddr: addr, CertFile: certFile, KeyFile: keyFile}))
}
// ListenTLSAuto starts a server listening at the specific nat address
// using key & certification taken from the letsencrypt.org 's servers
// it also starts a second 'http' server to redirect all 'http://$ADDR_HOSTNAME:80' to the' https://$ADDR'
//
// Notes:
// if you don't want the last feature you should use this method:
// iris.ListenTo(iris.ServerConfiguration{ListeningAddr: "mydomain.com:443", AutoTLS: true})
// it's a blocking function
// Limit : https://github.com/iris-contrib/letsencrypt/blob/master/lets.go#L142
//
// example: https://github.com/iris-contrib/examples/blob/master/letsencyrpt/main.go
func (s *Framework) ListenTLSAuto(addr string) {
if portIdx := strings.IndexByte(addr, ':'); portIdx == -1 {
addr += ":443"
}
addr = ServerParseAddr(addr)
// start a secondary server (HTTP) on port 80, this is a non-blocking func
// redirects all http to the main server which is tls/ssl on port :443
s.AddServer(ServerConfiguration{ListeningAddr: ":80", RedirectTo: "https://" + addr})
s.Must(s.ListenTo(ServerConfiguration{ListeningAddr: addr, AutoTLS: true}))
}
// ListenUNIX starts the process of listening to the new requests using a 'socket file', this works only on unix
//
// It panics on error if you need a func to return an error, use the ListenTo
// ex: iris.ListenUNIX(":8080", Mode: os.FileMode)
func ListenUNIX(addr string, mode os.FileMode) {
Default.ListenUNIX(addr, mode)
}
// ListenUNIX starts the process of listening to the new requests using a 'socket file', this works only on unix
//
// It panics on error if you need a func to return an error, use the ListenTo
// ex: ris.ListenUNIX(":8080", Mode: os.FileMode)
func (s *Framework) ListenUNIX(addr string, mode os.FileMode) {
s.Must(ListenTo(ServerConfiguration{ListeningAddr: addr, Mode: mode}))
}
// ListenVirtual is useful only when you want to test Iris, it doesn't starts the server but it configures and returns it
// initializes the whole framework but server doesn't listens to a specific net.Listener
// it is not blocking the app
func ListenVirtual(optionalAddr ...string) *Server {
return Default.ListenVirtual(optionalAddr...)
}
// ListenVirtual is useful only when you want to test Iris, it doesn't starts the server but it configures and returns it
// initializes the whole framework but server doesn't listens to a specific net.Listener
// it is not blocking the app
func (s *Framework) ListenVirtual(optionalAddr ...string) *Server {
s.Config.DisableBanner = true
cfg := DefaultServerConfiguration()
if len(optionalAddr) > 0 && optionalAddr[0] != "" {
cfg.ListeningAddr = optionalAddr[0]
}
cfg.Virtual = true
go func() {
s.Must(s.ListenTo(cfg))
}()
if ok := <-s.Available; !ok {
s.Logger.Panic("Unexpected error:Virtual server cannot start, please report this as bug!!")
}
close(s.Available)
return s.Servers.Main()
}
// Close terminates all the registered servers and returns an error if any
// if you want to panic on this error use the iris.Must(iris.Close())
func Close() error {
return Default.Close()
}
// Close terminates all the registered servers and returns an error if any
// if you want to panic on this error use the iris.Must(iris.Close())
func (s *Framework) Close() error {
s.Plugins.DoPreClose(s)
s.Available = make(chan bool)
return s.Servers.CloseAll()
}
// UseSessionDB registers a session database, you can register more than one
// accepts a session database which implements a Load(sid string) map[string]interface{} and an Update(sid string, newValues map[string]interface{})
// the only reason that a session database will be useful for you is when you want to keep the session's values/data after the app restart
@@ -922,10 +969,9 @@ func (s *Framework) URL(routeName string, args ...interface{}) (url string) {
if r == nil {
return
}
srv := s.Servers.Main()
scheme := srv.Scheme()
host := srv.Host()
scheme := s.Config.VScheme // if s.Config.VScheme was setted, that will be used instead of the real, in order to make easy to run behind nginx
host := s.Config.VHost // if s.Config.VHost was setted, that will be used instead of the real, in order to make easy to run behind nginx
arguments := args[0:]
// join arrays as arguments
@@ -1028,35 +1074,25 @@ func (s *Framework) SerializeToString(keyOrContentType string, obj interface{},
// NewTester Prepares and returns a new test framework based on the api
// is useful when you need to have more than one test framework for the same iris insttance, otherwise you can use the iris.Tester(t *testing.T)/variable.Tester(t *testing.T)
func NewTester(api *Framework, t *testing.T) *httpexpect.Expect {
srv := api.Servers.Main()
if srv == nil { // maybe the user called this after .Listen/ListenTLS/ListenUNIX, the tester can be used as standalone (with no running iris instance) or inside a running instance/app
srv = api.ListenVirtual(api.Config.Tester.ListeningAddr)
api.Set(OptionDisableBanner(true))
baseURL := ""
if api.fsrv == nil {
api.Build()
}
if !api.Config.Tester.ExplicitURL {
baseURL = api.Config.VScheme + api.Config.VHost
// if it's still empty then set it to the default server addr
if baseURL == "" {
baseURL = SchemeHTTP + DefaultServerAddr
}
opened := api.Servers.GetAllOpened()
h := srv.Handler
baseURL := srv.FullHost()
if len(opened) > 1 {
baseURL = ""
//we have more than one server, so we will create a handler here and redirect by registered listening addresses
h = func(reqCtx *fasthttp.RequestCtx) {
for _, s := range opened {
if strings.HasPrefix(reqCtx.URI().String(), s.FullHost()) { // yes on :80 should be passed :80 also, this is inneed for multiserver testing
s.Handler(reqCtx)
break
}
}
}
}
if api.Config.Tester.ExplicitURL {
baseURL = ""
}
testConfiguration := httpexpect.Config{
BaseURL: baseURL,
Client: &http.Client{
Transport: httpexpect.NewFastBinder(h),
Transport: httpexpect.NewFastBinder(api.Router),
Jar: httpexpect.NewJar(),
},
Reporter: httpexpect.NewAssertReporter(t),
@@ -1106,8 +1142,6 @@ type (
// main handlers
Handle(string, string, ...Handler) RouteNameFunc
HandleFunc(string, string, ...HandlerFunc) RouteNameFunc
// H_ is used to convert a context.IContext handler func to iris.HandlerFunc, is used only inside iris internal package to avoid import cycles
H_(string, string, func(context.IContext)) func(string)
API(string, HandlerAPI, ...HandlerFunc)
// http methods
@@ -1295,13 +1329,6 @@ func (api *muxAPI) HandleFunc(method string, registedPath string, handlersFn ...
return api.Handle(method, registedPath, convertToHandlers(handlersFn)...)
}
// H_ is used to convert a context.IContext handler func to iris.HandlerFunc, is used only inside iris internal package to avoid import cycles
func (api *muxAPI) H_(method string, registedPath string, fn func(context.IContext)) func(string) {
return api.HandleFunc(method, registedPath, func(ctx *Context) {
fn(ctx)
})
}
// API converts & registers a custom struct to the router
// receives two parameters
// first is the request path (string)
@@ -1756,7 +1783,7 @@ func (api *muxAPI) StaticWeb(reqPath string, systemPath string, stripSlashes int
if reqPath[len(reqPath)-1] != slashByte { // if / then /*filepath, if /something then /something/*filepath
reqPath += slash
}
//todo: fs.go
hasIndex := utils.Exists(systemPath + utils.PathSeparator + "index.html")
var indexNames []string
if hasIndex {

View File

@@ -1,14 +0,0 @@
package iris
/*
The most iris.go file implementation tested at other files like context_test, http_test, the untested are the Static methods, the favicon and some interfaces, which I already
tested them on production and I don't expect unexpected behavior but if you think we need more:
CONTRIBUTE & DISCUSSION ABOUT TESTS TO: https://github.com/iris-contrib/tests
*/
// Notes:
//
// We use Default := New() via initDefault() and not api := New() neither just Default. because we want to cover as much code as possible
// The tests are mostly end-to-end, except some features like plugins.
//

View File

@@ -56,6 +56,7 @@ func (t *testPluginEx) PreClose(*Framework) {
func ExamplePlugins_Add() {
initDefault()
Default.Set(OptionDisableBanner(true))
Plugins.Add(PreListenFunc(func(*Framework) {
fmt.Println("PreListen Func")
}))
@@ -72,9 +73,11 @@ func ExamplePlugins_Add() {
Plugins.Add(myplugin)
desc := Plugins.GetDescription(myplugin)
fmt.Println(desc)
go Listen(":8080")
ListenVirtual()
if ok := <-Available; ok {
Close()
}
// Output:
// GetName Struct

47
ssh.go
View File

@@ -102,7 +102,7 @@ func (s *SSHServer) bindTo(station *Framework) {
sshCommands := Commands{
Command{Name: "status", Description: "Prompts the status of the HTTP Server, is listening(started) or not(stopped).", Action: func(conn ssh.Channel) {
if station.Servers.Main() != nil && station.Servers.Main().IsListening() {
if station.IsRunning() {
statusRunningMsg(conn)
} else {
statusNotRunningMsg(conn)
@@ -115,29 +115,26 @@ func (s *SSHServer) bindTo(station *Framework) {
// Note for stop If you have opened a tab with Q route:
// in order to see that the http listener has closed you have to close your browser and re-navigate(browsers caches the tcp connection)
Command{Name: "stop", Description: "Stops the HTTP Server.", Action: func(conn ssh.Channel) {
srv := station.Servers.Main()
if srv != nil && srv.IsListening() {
srv.Close()
srv.listener = nil
if station.IsRunning() {
station.Close()
//srv.listener = nil used to reopen so let it setted
serverStoppedMsg(conn)
} else {
errServerNotReadyMsg(conn)
}
}},
Command{Name: "start", Description: "Starts the HTTP Server.", Action: func(conn ssh.Channel) {
srv := station.Servers.Main()
if !srv.IsListening() {
go srv.Open(srv.Handler)
if !station.IsRunning() {
go station.Reserve()
}
serverStartedMsg(conn)
}},
Command{Name: "restart", Description: "Restarts the HTTP Server.", Action: func(conn ssh.Channel) {
srv := station.Servers.Main()
if srv != nil && srv.IsListening() {
srv.Close()
srv.listener = nil
if station.IsRunning() {
station.Close()
//srv.listener = nil used to reopen so let it setted
}
go srv.Open(srv.Handler)
go station.Reserve()
serverRestartedMsg(conn)
}},
/* not ready yet
@@ -452,23 +449,9 @@ func (s *SSHServer) logf(format string, a ...interface{}) {
}
}
// parseHostname receives an addr of form host[:port] and returns the hostname part of it
// ex: localhost:8080 will return the `localhost`, mydomain.com:22 will return the 'mydomain'
func parseHostname(addr string) string {
idx := strings.IndexByte(addr, ':')
if idx == 0 {
// only port, then return 0.0.0.0
return "0.0.0.0"
} else if idx > 0 {
return addr[0:idx]
}
// it's already hostname
return addr
}
// parsePort receives an addr of form host[:port] and returns the port part of it
// ex: localhost:8080 will return the `8080`, mydomain.com will return the '22'
func parsePort(addr string) int {
// parsePortSSH receives an addr of form host[:port] and returns the port part of it
// ex: localhost:22 will return the `22`, mydomain.com will return the '22'
func parsePortSSH(addr string) int {
if portIdx := strings.IndexByte(addr, ':'); portIdx != -1 {
afP := addr[portIdx+1:]
p, err := strconv.Atoi(afP)
@@ -484,8 +467,8 @@ var standardCommands = Commands{Command{Name: "help", Description: "Opens up the
Command{Name: "exit", Description: "Exits from the terminal (if interactive shell)"}}
func (s *SSHServer) writeHelp(wr io.Writer) {
port := parsePort(s.Host)
hostname := parseHostname(s.Host)
port := parsePortSSH(s.Host)
hostname := ParseHostname(s.Host)
defer func() {
if r := recover(); r != nil {
// means that user-dev has old version of Go Programming Language in her/his machine, so print a message to the server terminal

View File

@@ -1,6 +0,0 @@
## Package information
This package contains helpful functions that iris, internally, uses
**That's it.**

View File

@@ -1,22 +0,0 @@
package utils
import (
"github.com/kataras/go-errors"
)
var (
// ErrNoZip returns an error with message: 'While creating file '+filename'. It's not a zip'
ErrNoZip = errors.New("While installing file '%s'. It's not a zip")
// ErrFileOpen returns an error with message: 'While opening a file. Trace: +specific error'
ErrFileOpen = errors.New("While opening a file. Trace: %s")
// ErrFileCreate returns an error with message: 'While creating a file. Trace: +specific error'
ErrFileCreate = errors.New("While creating a file. Trace: %s")
// ErrFileRemove returns an error with message: 'While removing a file. Trace: +specific error'
ErrFileRemove = errors.New("While removing a file. Trace: %s")
// ErrFileCopy returns an error with message: 'While copying files. Trace: +specific error'
ErrFileCopy = errors.New("While copying files. Trace: %s")
// ErrFileDownload returns an error with message: 'While downloading from +specific url. Trace: +specific error'
ErrFileDownload = errors.New("While downloading from %s. Trace: %s")
// ErrDirCreate returns an error with message: 'Unable to create directory on '+root dir'. Trace: +specific error
ErrDirCreate = errors.New("Unable to create directory on '%s'. Trace: %s")
)

View File

@@ -1,64 +0,0 @@
/* ticker.go: after version 1, we don't need this atm, but we keep it. */
package utils
import (
"time"
)
// Ticker is the timer which is used in cache
type Ticker struct {
ticker *time.Ticker
started bool
tickHandlers []func()
}
// NewTicker returns a new Ticker
func NewTicker() *Ticker {
return &Ticker{tickHandlers: make([]func(), 0), started: false}
}
// OnTick add event handlers/ callbacks which are called on each timer's tick
func (c *Ticker) OnTick(h func()) {
c.tickHandlers = append(c.tickHandlers, h)
}
// Start starts the timer and execute all listener's when tick
func (c *Ticker) Start(duration time.Duration) {
if c.started {
return
}
if c.ticker != nil {
panic("Iris Ticker: Cannot re-start a cache timer, if you stop it, it is not recommented to resume it,\n Just create a new CacheTimer.")
}
c.ticker = time.NewTicker(duration)
go func() {
for t := range c.ticker.C {
_ = t
// c.mu.Lock()
// c.mu.Unlock()
//I can make it a clojure to handle only handlers that are registed before .start() but we are ok with this, it is not map no need to Lock, for now.
for i := range c.tickHandlers {
c.tickHandlers[i]()
}
}
}()
c.started = true
}
// Stop stops the ticker
func (c *Ticker) Stop() {
if c.started {
c.ticker.Stop()
c.started = false
}
}
// ITick is the interface which all ticker's listeners must implement
type ITick interface {
OnTick()
}