Home Download Docs Code Community
     1	/*
     2	Copyright 2017 The Perkeep Authors
     3	
     4	Licensed under the Apache License, Version 2.0 (the "License");
     5	you may not use this file except in compliance with the License.
     6	You may obtain a copy of the License at
     7	
     8	     http://www.apache.org/licenses/LICENSE-2.0
     9	
    10	Unless required by applicable law or agreed to in writing, software
    11	distributed under the License is distributed on an "AS IS" BASIS,
    12	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13	See the License for the specific language governing permissions and
    14	limitations under the License.
    15	*/
    16	
    17	package listen // import "perkeep.org/pkg/webserver/listen"
    18	
    19	import (
    20		"errors"
    21		"flag"
    22		"fmt"
    23		"net"
    24		"os"
    25		"runtime"
    26		"strconv"
    27		"strings"
    28	)
    29	
    30	// NewFlag returns a flag that implements the flag.Value interface.
    31	func NewFlag(flagName, defaultValue string, serverType string) *Addr {
    32		addr := &Addr{
    33			s: defaultValue,
    34		}
    35		flag.Var(addr, flagName, Usage(serverType))
    36		return addr
    37	}
    38	
    39	// Listen is a replacement for net.Listen and supports
    40	//
    41	//	port, :port, ip:port, FD:<fd_num>
    42	//
    43	// Listeners are always TCP.
    44	func Listen(addr string) (net.Listener, error) {
    45		a := &Addr{s: addr}
    46		return a.Listen()
    47	}
    48	
    49	// Usage returns a descriptive usage message for a flag given the name
    50	// of thing being addressed.
    51	func Usage(name string) string {
    52		if name == "" {
    53			name = "Listen address"
    54		}
    55		if !strings.HasSuffix(name, " address") {
    56			name += " address"
    57		}
    58		return name + "; may be port, :port, ip:port, or FD:<fd_num>"
    59	}
    60	
    61	// Addr is a flag variable.  Use like:
    62	//
    63	//	var webPort listen.Addr
    64	//	flag.Var(&webPort, "web_addr", listen.Usage("Web server address"))
    65	//	flag.Parse()
    66	//	webListener, err := webPort.Listen()
    67	type Addr struct {
    68		s   string
    69		ln  net.Listener
    70		err error
    71	}
    72	
    73	func (a *Addr) String() string {
    74		return a.s
    75	}
    76	
    77	// Set implements the flag.Value interface.
    78	func (a *Addr) Set(v string) error {
    79		a.s = v
    80	
    81		if strings.HasPrefix(v, "FD:") {
    82			fdStr := v[len("FD:"):]
    83			fd, err := strconv.ParseUint(fdStr, 10, 32)
    84			if err != nil {
    85				return fmt.Errorf("invalid file descriptor %q: %v", fdStr, err)
    86			}
    87			return a.listenOnFD(uintptr(fd))
    88		}
    89	
    90		ipPort := v
    91		if isPort(v) {
    92			ipPort = ":" + v
    93		}
    94	
    95		_, _, err := net.SplitHostPort(ipPort)
    96		if err != nil {
    97			return fmt.Errorf("invalid PORT or IP:PORT %q: %v", v, err)
    98		}
    99		a.ln, err = net.Listen("tcp", ipPort)
   100		return err
   101	}
   102	
   103	func isPort(s string) bool {
   104		_, err := strconv.ParseUint(s, 10, 16)
   105		return err == nil
   106	}
   107	
   108	func (a *Addr) listenOnFD(fd uintptr) (err error) {
   109		if runtime.GOOS == "windows" {
   110			panic("listenOnFD unsupported on Windows")
   111		}
   112		f := os.NewFile(fd, fmt.Sprintf("fd #%d from process parent", fd))
   113		a.ln, err = net.FileListener(f)
   114		return
   115	}
   116	
   117	var _ flag.Value = (*Addr)(nil)
   118	
   119	// Listen returns the address's TCP listener.
   120	func (a *Addr) Listen() (net.Listener, error) {
   121		// Start the listener now, if there's a default
   122		// and nothing's called Set yet.
   123		if a.err == nil && a.ln == nil && a.s != "" {
   124			if err := a.Set(a.s); err != nil {
   125				return nil, err
   126			}
   127		}
   128		if a.err != nil {
   129			return nil, a.err
   130		}
   131		if a.ln != nil {
   132			return a.ln, nil
   133		}
   134		return nil, errors.New("listen: no error or listener")
   135	}
Website layout inspired by memcached.
Content by the authors.