mirror of
https://github.com/minio/minio.git
synced 2025-03-28 08:20:58 -04:00
Server http and https on the same port using a customized server (#2247)
This commit is contained in:
parent
6c2fb19ed7
commit
b0b919a1d6
@ -91,15 +91,17 @@ type serverCmdConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// configureServer configure a new server instance
|
// configureServer configure a new server instance
|
||||||
func configureServer(srvCmdConfig serverCmdConfig) *http.Server {
|
func configureServer(srvCmdConfig serverCmdConfig) *MuxServer {
|
||||||
// Minio server config
|
// Minio server config
|
||||||
apiServer := &http.Server{
|
apiServer := &MuxServer{
|
||||||
Addr: srvCmdConfig.serverAddr,
|
Server: http.Server{
|
||||||
// Adding timeout of 10 minutes for unresponsive client connections.
|
Addr: srvCmdConfig.serverAddr,
|
||||||
ReadTimeout: 10 * time.Minute,
|
// Adding timeout of 10 minutes for unresponsive client connections.
|
||||||
WriteTimeout: 10 * time.Minute,
|
ReadTimeout: 10 * time.Minute,
|
||||||
Handler: configureServerHandler(srvCmdConfig),
|
WriteTimeout: 10 * time.Minute,
|
||||||
MaxHeaderBytes: 1 << 20,
|
Handler: configureServerHandler(srvCmdConfig),
|
||||||
|
MaxHeaderBytes: 1 << 20,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns configured HTTP server.
|
// Returns configured HTTP server.
|
||||||
@ -260,7 +262,7 @@ func serverMain(c *cli.Context) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Fetch endpoints which we are going to serve from.
|
// Fetch endpoints which we are going to serve from.
|
||||||
endPoints := finalizeEndpoints(tls, apiServer)
|
endPoints := finalizeEndpoints(tls, &apiServer.Server)
|
||||||
|
|
||||||
// Prints the formatted startup message.
|
// Prints the formatted startup message.
|
||||||
printStartupMessage(endPoints)
|
printStartupMessage(endPoints)
|
||||||
|
214
server-mux.go
Normal file
214
server-mux.go
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2016 Minio, Inc.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var defaultHTTP2Methods = []string{
|
||||||
|
"PRI",
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultHTTP1Methods = []string{
|
||||||
|
"OPTIONS",
|
||||||
|
"GET",
|
||||||
|
"HEAD",
|
||||||
|
"POST",
|
||||||
|
"PUT",
|
||||||
|
"DELETE",
|
||||||
|
"TRACE",
|
||||||
|
"CONNECT",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnBuf - contains network buffer to record data
|
||||||
|
type ConnBuf struct {
|
||||||
|
buffer []byte
|
||||||
|
unRead bool
|
||||||
|
offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
// MuxConn - implements a Read() which streams twice the firs bytes from
|
||||||
|
// the incoming connection, to help peeking protocol
|
||||||
|
type MuxConn struct {
|
||||||
|
net.Conn
|
||||||
|
lastError error
|
||||||
|
dataBuf ConnBuf
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMuxConn - creates a new MuxConn instance
|
||||||
|
func NewMuxConn(c net.Conn) *MuxConn {
|
||||||
|
h1 := longestWord(defaultHTTP1Methods)
|
||||||
|
h2 := longestWord(defaultHTTP2Methods)
|
||||||
|
max := h1
|
||||||
|
if h2 > max {
|
||||||
|
max = h2
|
||||||
|
}
|
||||||
|
return &MuxConn{Conn: c, dataBuf: ConnBuf{buffer: make([]byte, max+1)}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeekProtocol - reads the first bytes, then checks if it is similar
|
||||||
|
// to one of the default http methods
|
||||||
|
func (c *MuxConn) PeekProtocol() string {
|
||||||
|
var n int
|
||||||
|
n, c.lastError = c.Conn.Read(c.dataBuf.buffer)
|
||||||
|
if n == 0 || (c.lastError != nil && c.lastError != io.EOF) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
c.dataBuf.unRead = true
|
||||||
|
for _, m := range defaultHTTP1Methods {
|
||||||
|
if strings.HasPrefix(string(c.dataBuf.buffer), m) {
|
||||||
|
return "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, m := range defaultHTTP2Methods {
|
||||||
|
if strings.HasPrefix(string(c.dataBuf.buffer), m) {
|
||||||
|
return "http2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "tls"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read - streams the MuxConn buffer when reset flag is activated, otherwise
|
||||||
|
// streams from the incoming network connection
|
||||||
|
func (c *MuxConn) Read(b []byte) (int, error) {
|
||||||
|
if c.dataBuf.unRead {
|
||||||
|
n := copy(b, c.dataBuf.buffer[c.dataBuf.offset:])
|
||||||
|
c.dataBuf.offset += n
|
||||||
|
if c.dataBuf.offset == len(c.dataBuf.buffer) {
|
||||||
|
// We finished copying all c.buffer, reset all
|
||||||
|
c.dataBuf.unRead = false
|
||||||
|
c.dataBuf.offset = 0
|
||||||
|
c.dataBuf.buffer = c.dataBuf.buffer[:]
|
||||||
|
if n < len(b) {
|
||||||
|
// Continue copying from socket if b still has room for data
|
||||||
|
tmpBuffer := make([]byte, len(b)-n-1)
|
||||||
|
nr, err := c.Conn.Read(tmpBuffer)
|
||||||
|
for idx, val := range tmpBuffer {
|
||||||
|
b[n+idx] = val
|
||||||
|
}
|
||||||
|
return n + nr, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We here return the last error
|
||||||
|
return n, c.lastError
|
||||||
|
}
|
||||||
|
return c.Conn.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MuxListener - encapuslates the standard net.Listener to inspect
|
||||||
|
// the communication protocol upon network connection
|
||||||
|
type MuxListener struct {
|
||||||
|
net.Listener
|
||||||
|
config *tls.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMuxListener - creates new MuxListener, returns error when cert/key files are not found
|
||||||
|
// or invalid
|
||||||
|
func NewMuxListener(listener net.Listener, certPath, keyPath string) (MuxListener, error) {
|
||||||
|
var err error
|
||||||
|
config := &tls.Config{}
|
||||||
|
if config.NextProtos == nil {
|
||||||
|
config.NextProtos = []string{"http/1.1", "h2"}
|
||||||
|
}
|
||||||
|
config.Certificates = make([]tls.Certificate, 1)
|
||||||
|
config.Certificates[0], err = tls.LoadX509KeyPair(certPath, keyPath)
|
||||||
|
if err != nil {
|
||||||
|
return MuxListener{}, err
|
||||||
|
}
|
||||||
|
return MuxListener{Listener: listener, config: config}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept - peek the protocol to decide if we should wrap the
|
||||||
|
// network stream with the TLS server
|
||||||
|
func (l MuxListener) Accept() (net.Conn, error) {
|
||||||
|
c, err := l.Listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return c, err
|
||||||
|
}
|
||||||
|
cmux := NewMuxConn(c)
|
||||||
|
protocol := cmux.PeekProtocol()
|
||||||
|
if protocol == "tls" {
|
||||||
|
return tls.Server(cmux, l.config), nil
|
||||||
|
}
|
||||||
|
return cmux, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MuxServer - the main mux server
|
||||||
|
type MuxServer struct {
|
||||||
|
http.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndServeTLS - similar to the http.Server version. However, it has the
|
||||||
|
// ability to redirect http requests to the correct HTTPS url if the client
|
||||||
|
// mistakenly initiates a http connection over the https port
|
||||||
|
func (m *MuxServer) ListenAndServeTLS(certFile, keyFile string) error {
|
||||||
|
listener, err := net.Listen("tcp", m.Server.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mux, err := NewMuxListener(listener, mustGetCertFile(), mustGetKeyFile())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = http.Serve(mux,
|
||||||
|
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// We reach here when MuxListener.MuxConn is not wrapped with tls.Server
|
||||||
|
if r.TLS == nil {
|
||||||
|
u := url.URL{
|
||||||
|
Scheme: "https",
|
||||||
|
Opaque: r.URL.Opaque,
|
||||||
|
User: r.URL.User,
|
||||||
|
Host: r.Host,
|
||||||
|
Path: r.URL.Path,
|
||||||
|
RawQuery: r.URL.RawQuery,
|
||||||
|
Fragment: r.URL.Fragment,
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, u.String(), http.StatusMovedPermanently)
|
||||||
|
} else {
|
||||||
|
// Execute registered handlers
|
||||||
|
m.Server.Handler.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndServe - Same as the http.Server version
|
||||||
|
func (m *MuxServer) ListenAndServe() error {
|
||||||
|
return m.Server.ListenAndServe()
|
||||||
|
}
|
||||||
|
|
||||||
|
func longestWord(strings []string) int {
|
||||||
|
maxLen := 0
|
||||||
|
for _, m := range defaultHTTP1Methods {
|
||||||
|
if maxLen < len(m) {
|
||||||
|
maxLen = len(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, m := range defaultHTTP2Methods {
|
||||||
|
if maxLen < len(m) {
|
||||||
|
maxLen = len(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxLen
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user