├── .idea ├── dictionaries │ └── florin.xml ├── gopherconuk.iml ├── inspectionProfiles │ └── Project_Default.xml ├── modules.xml ├── runConfigurations │ ├── DB.xml │ └── gcuk.xml ├── sqldialects.xml └── vcs.xml ├── Dockerfile ├── Dockerfile.db ├── LICENSE ├── README.md ├── certs ├── docker.localhost.cert ├── docker.localhost.key ├── local.localhost.cert └── local.localhost.key ├── go.mod ├── go.sum ├── homepage ├── home.go └── home_test.go ├── main.go ├── requests.http └── server └── server.go /.idea/dictionaries/florin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | gcuk 5 | gopherconuk 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/gopherconuk.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/runConfigurations/DB.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /.idea/runConfigurations/gcuk.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 26 | 35 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /.idea/sqldialects.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.11beta2-alpine3.8 AS build-env 2 | 3 | # Allow Go to retrive the dependencies for the build step 4 | RUN apk add --no-cache git 5 | 6 | # Secure against running as root 7 | RUN adduser -D -u 10000 florin 8 | RUN mkdir /gopherconuk/ && chown florin /gopherconuk/ 9 | USER florin 10 | 11 | WORKDIR /gopherconuk/ 12 | ADD . /gopherconuk/ 13 | 14 | # Compile the binary, we don't want to run the cgo resolver 15 | RUN CGO_ENABLED=0 go build -o /gopherconuk/gcuk . 16 | 17 | # final stage 18 | FROM alpine:3.8 19 | 20 | # Secure against running as root 21 | RUN adduser -D -u 10000 florin 22 | USER florin 23 | 24 | WORKDIR / 25 | COPY --from=build-env /gopherconuk/certs/docker.localhost.* / 26 | COPY --from=build-env /gopherconuk/gcuk / 27 | 28 | EXPOSE 8080 29 | 30 | CMD ["/gcuk"] 31 | -------------------------------------------------------------------------------- /Dockerfile.db: -------------------------------------------------------------------------------- 1 | FROM postgres:10.4-alpine 2 | 3 | ENV POSTGRES_USER postgres 4 | ENV POSTGRES_PASSWORD postgres 5 | ENV POSTGRES_DB gcuk 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Florin Pățan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go micro-service in ~30 minutes 2 | 3 | This is a Go micro-service written from scratch. 4 | 5 | It shows how to use [net/http](https://godoc.org/net/http), and how to structure a Go project. 6 | 7 | It relies on Go 1.11 Beta 2 and the upcoming "Go modules" (formerly known as "vgo") support. 8 | 9 | Dependency injection is used to insert a logger instance into the handler. 10 | 11 | You can also notice how the test is constructed in order to provide testing for the handler. 12 | 13 | A Docker container is available, thanks to the Dockerfile. It shows how to construct such containers. 14 | 15 | ## How to use 16 | 17 | Because this project uses go modules, as long as you are using Go 1.11 Beta 2+ or Go 1.10 with vgo support, 18 | you should be ok. 19 | 20 | Clone this anywhere in your computer and create a project in your editor. I'm using [GoLand IDE](https://jetbrains.com/go) in order to work 21 | on the project during the presentation as well as have support for go modules. 22 | 23 | The bundled, self-signed, certificates are bound to either ` dev.localhost:8080 ` or ` docker.localhost:8080 `. I obviously 24 | do not recommend using these in production. 25 | 26 | ## Presentation link 27 | 28 | I created this as part of the presentation at [GopherCon UK 18](https://www.gophercon.co.uk/). 29 | 30 | The link for the video will be updated here when the presentation is out. 31 | 32 | ## References 33 | 34 | ### Structuring Go applications 35 | 36 | In order to learn how to approach package design in Go, you can read the following resources: 37 | 38 | - [Style guideline for Go packages - JBD](https://rakyll.org/style-packages/) 39 | - [Standard Package Layout - Ben Johnson](https://medium.com/@benbjohnson/standard-package-layout-7cdbc8391fc1#.ds38va3pp) 40 | - [Go best practices, six years in - Peter Bourgon](https://peter.bourgon.org/go-best-practices-2016/#repository-structure) 41 | 42 | Once done, this article will help you understand the [Design Philosophy On Packaging by William Kennedy](https://www.ardanlabs.com/blog/2017/02/design-philosophy-on-packaging.html). 43 | 44 | ### Exposing Go applications to the Internet 45 | 46 | [This article](https://blog.cloudflare.com/exposing-go-on-the-internet/) describes how you can start approaching 47 | 48 | ## Thank you 49 | 50 | I would like to thank you [William "Bill" Kennedy](https://twitter.com/goinggodotnet) for the inspiration he provided on 51 | getting me to do this talk. 52 | 53 | 54 | ## License 55 | 56 | This project is under the MIT license. Please see the [LICENSE](LICENSE) file for more details. 57 | -------------------------------------------------------------------------------- /certs/docker.localhost.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDCTCCAfGgAwIBAgIJALznbLlRwEspMA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNV 3 | BAMMEGRvY2tlci5sb2NhbGhvc3QwHhcNMTgwODAyMjI0NDE2WhcNMjgwNzMwMjI0 4 | NDE2WjAbMRkwFwYDVQQDDBBkb2NrZXIubG9jYWxob3N0MIIBIjANBgkqhkiG9w0B 5 | AQEFAAOCAQ8AMIIBCgKCAQEAr6YgpwNNs37fjXw1bpKqfdkrSCTi7z3ecxKzzxHQ 6 | x/IqRUuWEhoJ6s64vFJkPM3YnkzQI8FlFLFUPUISuOrJBdFyyq/QrEHDhj8FElmk 7 | jLaNk62p8J3BE7imNtjoUHT7Ly7XPEoWFaP+FpsIX6i8JilJykyjEpmLjTe0sRxo 8 | OtUPATzg5RWZhy85GBqmQLgYxlkIV5Kc58NLWax1Onsl7r9WL/BNyDOtoAQKy9Z7 9 | oG5hwAzNzK9DdMvQnxeoHyqo4otF8t0UBqDzWCrJ66GtOFQcu5wBuypciYU7UeHf 10 | nT6XyCPWFYPLnkOfwEuSdXcu6gI9xvg6kJ9ODU9UnsCOZQIDAQABo1AwTjAdBgNV 11 | HQ4EFgQUCTKuk7Kbc89VJOMvZ4HAXHg9mqEwHwYDVR0jBBgwFoAUCTKuk7Kbc89V 12 | JOMvZ4HAXHg9mqEwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAW+pY 13 | WTax46RFSWTPOmn6G748NXWiRdsbcWc6Q6NHR1E6le48GqfOAEN5Pub7JQAUMXfC 14 | ufn0yNr/cCeKgXNVBsE6hr+dw2snzh1Q3NIseXM4imGbQvTM9W8RRclULViSUta/ 15 | D/b0JtcdWyACAVwvw6nQPcW5rGA9y+ZTSiI+EwckYVhBhqDcSmMrnCcbtsLjjUB3 16 | DazSfZXfT/RjZw5vcoCDVRo/hidWNQ5/2fGysW7anSWo18HUIvdy9sMOiTAXchVa 17 | SKIZvplfjPJBHpNVuqMSranN5Khd49rx1li8ZAnpLOVc5+tWOwSKzbW/C/4mZFAo 18 | f5arbOZ7NQWbJzZn7w== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /certs/docker.localhost.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAr6YgpwNNs37fjXw1bpKqfdkrSCTi7z3ecxKzzxHQx/IqRUuW 3 | EhoJ6s64vFJkPM3YnkzQI8FlFLFUPUISuOrJBdFyyq/QrEHDhj8FElmkjLaNk62p 4 | 8J3BE7imNtjoUHT7Ly7XPEoWFaP+FpsIX6i8JilJykyjEpmLjTe0sRxoOtUPATzg 5 | 5RWZhy85GBqmQLgYxlkIV5Kc58NLWax1Onsl7r9WL/BNyDOtoAQKy9Z7oG5hwAzN 6 | zK9DdMvQnxeoHyqo4otF8t0UBqDzWCrJ66GtOFQcu5wBuypciYU7UeHfnT6XyCPW 7 | FYPLnkOfwEuSdXcu6gI9xvg6kJ9ODU9UnsCOZQIDAQABAoIBAQCXnQUIREC4k+Xx 8 | IWqZk/2X/XFvp28+5J9zoowUS6N0QyKbh7/0xgcTZ/Zrj464MVIuac0rX86ZSuuU 9 | qMiyVu7Hl9/G9nVfirBz20fMbBYt45FXDIroh7LWDAdkxlvYvvDl7D+a6bQgX9p9 10 | trpGn8YwBmmMNhR4hJLBhXjxe7y4Mis6D90RDkE+6ZXFe9gbpdppaAZ/VVHVzrXq 11 | 0yBolGS0e+64bZ8bjokDhyuRyVpjQTQeO9bTxvSCn2rrQ9WbrQW1HxFrY7WLdioX 12 | sqNVF1J4PE2Ox1eTmirE3iTE34nI99rGitOOudx7R++kSbkDB2JyEttnDTRHnA+3 13 | vPIRJcAtAoGBAODZmfQYGeItplNJgyxwZBCG7ut7X23g62EgGMwPxpIBFeWgO7q0 14 | uzchEauK4qWlFulr7k2vGo7NBBP3wsgBQyx81Vnw5V1XXktC5sdBShymMWMt/S8G 15 | 3+2whNTNzf9n9bSavBIWm+b5mezDPVft+IprARjt9V0zBJXEwrJ1XKcvAoGBAMf7 16 | lv/3Yp+eF22Va0AgLOepz3Ab0KHdKDTn62BhgBl3PwOKrRGjb4k+mT4zNq0JjtZR 17 | 9MezZ+S2iJPMoaL8Gc/Kpa/KPsbpfsGrqmOzhW6feSFOyPeJgPn9zYpkwpmvkYWh 18 | S3aH+aZpxE8kfkJdK8HBbXennPowZeys5xR0uL6rAoGAJF0s19w5+2lUl+2wqDQ5 19 | Cq833p+iTFvu1VNij0YR4DUKvItQfZ6TFJRlji+0/gMYFIIfFTM3gVbIzQ3p03zY 20 | x04dFyGtho4FsvhL6He3q7B5FkguxTdAael9YgkywpamlyHbPrWnWxCNA0yEFD9p 21 | TcGGeOirIPqKSEp9Dm2KoYMCgYBp7ZAMQWOSHNFYEF9g9ioTA9WHEpSuSFD1xSre 22 | QIbz9kAAhkIqJ6H61pehZMs/WOK6D2tTJGqRsUYi06+Cf3rEoBinRr996UxlFaMJ 23 | cJvq/rnQtMSqqcERqihnd/vEIEU6vTcVr+zDAx8itLfUOznkRarQSI5Tz7MNbIBy 24 | S2lYRQKBgDeo2Gv1UEJH59mjz5jIOlRxo03+c+DPl6088BcErN9/CTU+WlqxLhLa 25 | u02RO3KCe3GO2KZS9E5LZzanLDdfynj2HFjf0MRGxcHtv0/Odr8UEg3lfah6GL2Y 26 | dEGAfQNZuqaIf3NX7/oK1wyaajZNfCN6vKbJbCnfqx6hDuiTIw6X 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /certs/local.localhost.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDBzCCAe+gAwIBAgIJAM3zCUrPG4X1MA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNV 3 | BAMMD2xvY2FsLmxvY2FsaG9zdDAeFw0xODA4MDIyMjQzMjhaFw0yODA3MzAyMjQz 4 | MjhaMBoxGDAWBgNVBAMMD2xvY2FsLmxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB 5 | BQADggEPADCCAQoCggEBALPCrtIsm4iIK2sv5NO13LZ1qqlo32qEMLisCm3b3k4N 6 | jEulO7QqA2ke861H6OxWL3IEci7U7Vtt0rSYqniRPhTV17sq3IQUN+vrzM2EOJGG 7 | +rD8cpJqgUOEb3MUieql0OHXRRNFox9OFVRPGfn5E/CcotVoT6ym6qU3qUGYjfGu 8 | MKMDEXyV63EkwrUUH6ry/8Ykfe49WldZMikrxbm0s7Q7PvGh7Ke4MFRVI+nilZ0T 9 | OqncawcGn1mDR3880rWxZMwVyL44lX7axLOcZCreQE/RSGG+XfueIB06tP3tfFFh 10 | Wuz0hgLoACgfQQHNa0bcVG0XvqkxzQqsN0U8yHE7G3cCAwEAAaNQME4wHQYDVR0O 11 | BBYEFMfDqZEHnd3wQyPLg+S3pSxD6xkrMB8GA1UdIwQYMBaAFMfDqZEHnd3wQyPL 12 | g+S3pSxD6xkrMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKeqdY6O 13 | +hY8Igx/LQ44XUDNqnCJuyqg8JDibadbsqsaxZtWt56n8XfTw47R4SfeqD7TwhLu 14 | PlX7uQhh+fd8btBVmsmOX79ENtvRVJArmJzc77ATdBwA13rMiCD8UEudf6T9Vef8 15 | o0H0vgWXju2ovdrMFTK7SONcZgybTDr9n/JPMrghsoGm1kHL3D2r41SGVeM2705R 16 | JKJ6VwAcLBydWe2uZTskeR0O0POEzQzTJfodmIt9l2G9mObCQygal6SsPTBARVOk 17 | BcYix2JW2q3XsV1bT9DbVk3jNGdcNhhulPaH/jfmOgkn68TF53Rriw+fsHU1o3b1 18 | InheFMfx+4NP5Sc= 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /certs/local.localhost.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAs8Ku0iybiIgray/k07XctnWqqWjfaoQwuKwKbdveTg2MS6U7 3 | tCoDaR7zrUfo7FYvcgRyLtTtW23StJiqeJE+FNXXuyrchBQ36+vMzYQ4kYb6sPxy 4 | kmqBQ4RvcxSJ6qXQ4ddFE0WjH04VVE8Z+fkT8Jyi1WhPrKbqpTepQZiN8a4wowMR 5 | fJXrcSTCtRQfqvL/xiR97j1aV1kyKSvFubSztDs+8aHsp7gwVFUj6eKVnRM6qdxr 6 | BwafWYNHfzzStbFkzBXIvjiVftrEs5xkKt5AT9FIYb5d+54gHTq0/e18UWFa7PSG 7 | AugAKB9BAc1rRtxUbRe+qTHNCqw3RTzIcTsbdwIDAQABAoIBADoCYGIozQRRVRmg 8 | +gosDQ7AiBNlaXu5LvPZaQAszN+JVbXvm4W3bSRWPbK0mwDxixwTPTnJstMnbLCk 9 | 95Yy4MLeg2C+Iy1oTOeQT6YevhaQZYQJ43tGbXJ5YXHUtfEIE5Fd48elWE+45f8w 10 | C6T0cS/bjFyr7dI8h0pL3Q3zChNsZYSO7yUhk3t5h9uVcl/3so64ctTBs/wpw3n+ 11 | fxEaHUOOzbFI6J6KYVD0AM41kza6NY6MBySEVgdfbDsNDfFxu2/KAC2oV9vjZyJ0 12 | hWtkEVyV5mNVnC4Z61NdXdZRGG1Z9fFIZi9zeb5iWH2lValT7AQpUZEuw9ch0omP 13 | NQ6qeWECgYEA2TR0xejHdayZwWYq1BndRzQNLAekpUfXMXEuRsm2jLcclrBs/dbJ 14 | sfUKD1I5Z3A0W+94z1AUOuSKj3WzLqB8Ta1IGjDEJ58Y36ubVgoTVTCHE/xeGr74 15 | pSxkJarb9Zj1dNVyfzRDppmvlVxkEsexe79wy8nx9mxnsWIDoNaegNECgYEA094b 16 | NVcnXF8nn1URoNB5yIT7U9Kqd8O5WW+qZaflSPhl7RF4mUR+gPJMt6zNj8h1N7xw 17 | lpBHa6mlMJ4R1Gwrstbu5bFveN/TrQ+08WrFH5NbjQXiB5z5OgtsbKL1pwjEXiFO 18 | +dIev8TuFIts0OSiEoyjjPUbMo3JCTSnaHALqccCgYBwgveqPPZ1Qj8zK1UsIWm6 19 | JIBeOmdTJm+WMOajzIE/SusIM8OBie1YvXHElfceWcUFinquCPExwIj13yY+FOvO 20 | 4N/kkTZKv+MGmcIRQ47YJhcShzvH5vytstz9lg/ynJUpPBffRJd2iU2mtQExqTeK 21 | vpQTPDyWRXlBLWBrxEC+wQKBgQC4r6+WIuAb5JLckMJqJfFTO6D+o/CXsBUdgnrO 22 | XLpBjs8DBizTpmKEMzNvFYKnqP2NVYXrqd+oWVy3ccNnIdhB8JyVu8PJYYvbG4sG 23 | u6UUblbrKsqrCkozMAwW80MilUDgMTbwDRti3d8QJ632tqcVdmWHlY5c1j39+VlJ 24 | cAqqtwKBgCQtB98sm7y2n6tU8TAm+miPBWUhbjVeBhlgOu8FoAI1ve+nhPOOPsc+ 25 | P/ee+Ru0x7XyyR4omG1D5kZqTIzw2aHzxMU40dQbCSe1EGbmEAxz47DuIn0iRU92 26 | kg4/DS7+n6VyGiYkg1YxpTqF4BLAlsMExNCcwTLEhHj743woVtlz 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module gopherconuk 2 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlsniper/gopherconuk/fc9bbccfa90017929aee4126d618cbc3d84b30af/go.sum -------------------------------------------------------------------------------- /homepage/home.go: -------------------------------------------------------------------------------- 1 | package homepage 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | const message = "Hello GopherCon UK 2018!" 10 | 11 | type Handlers struct { 12 | logger *log.Logger 13 | } 14 | 15 | func (h *Handlers) Home(w http.ResponseWriter, r *http.Request) { 16 | w.Header().Set("Content-Type", "text/plain; charset=utf-8") 17 | w.WriteHeader(http.StatusOK) 18 | w.Write([]byte(message)) 19 | } 20 | 21 | func (h *Handlers) Logger(next http.HandlerFunc) http.HandlerFunc { 22 | return func(w http.ResponseWriter, r *http.Request) { 23 | startTime := time.Now() 24 | defer h.logger.Printf("request processed in %s\n", time.Now().Sub(startTime)) 25 | next(w, r) 26 | } 27 | } 28 | func (h *Handlers) SetupRoutes(mux *http.ServeMux) { 29 | mux.HandleFunc("/", h.Logger(h.Home)) 30 | } 31 | 32 | func NewHandlers(logger *log.Logger) *Handlers { 33 | return &Handlers{ 34 | logger: logger, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /homepage/home_test.go: -------------------------------------------------------------------------------- 1 | package homepage 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | ) 8 | 9 | func TestHandlers_Handler(t *testing.T) { 10 | tests := []struct { 11 | name string 12 | in *http.Request 13 | out *httptest.ResponseRecorder 14 | expectedStatus int 15 | expectedBody string 16 | }{ 17 | { 18 | name: "good", 19 | in: httptest.NewRequest("GET", "/", nil), 20 | out: httptest.NewRecorder(), 21 | expectedStatus: http.StatusOK, 22 | expectedBody: message, 23 | }, 24 | } 25 | 26 | for _, test := range tests { 27 | test := test 28 | t.Run(test.name, func(t *testing.T) { 29 | h := NewHandlers(nil) 30 | h.Home(test.out, test.in) 31 | if test.out.Code != test.expectedStatus { 32 | t.Logf("expected: %d\ngot: %d\n", test.expectedStatus, test.out.Code) 33 | t.Fail() 34 | } 35 | 36 | body := test.out.Body.String() 37 | if body != test.expectedBody { 38 | t.Logf("expected: %s\ngot: %s\n", test.expectedBody, body) 39 | t.Fail() 40 | } 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "gopherconuk/homepage" 5 | "gopherconuk/server" 6 | "log" 7 | "net/http" 8 | "os" 9 | ) 10 | 11 | var ( 12 | GcukCertFile = os.Getenv("GCUK_CERT_FILE") 13 | GcukKeyFile = os.Getenv("GCUK_KEY_FILE") 14 | GcukServiceAddr = os.Getenv("GCUK_SERVICE_ADDR") 15 | ) 16 | 17 | func main() { 18 | logger := log.New(os.Stdout, "gcuk ", log.LstdFlags|log.Lshortfile) 19 | 20 | h := homepage.NewHandlers(logger) 21 | 22 | mux := http.NewServeMux() 23 | h.SetupRoutes(mux) 24 | 25 | srv := server.New(mux, GcukServiceAddr) 26 | 27 | logger.Println("server starting") 28 | err := srv.ListenAndServeTLS(GcukCertFile, GcukKeyFile) 29 | if err != nil { 30 | logger.Fatalf("server failed to start: %v", err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /requests.http: -------------------------------------------------------------------------------- 1 | ### Local connection 2 | GET https://local.localhost:8080/ 3 | 4 | ### 5 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "crypto/tls" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | func New(mux *http.ServeMux, serverAddress string) *http.Server { 10 | // See https://blog.cloudflare.com/exposing-go-on-the-internet/ for details 11 | // about these settings 12 | tlsConfig := &tls.Config{ 13 | // Causes servers to use Go's default cipher suite preferences, 14 | // which are tuned to avoid attacks. Does nothing on clients. 15 | PreferServerCipherSuites: true, 16 | // Only use curves which have assembly implementations 17 | CurvePreferences: []tls.CurveID{ 18 | tls.CurveP256, 19 | tls.X25519, // Go 1.8 only 20 | }, 21 | 22 | MinVersion: tls.VersionTLS12, 23 | CipherSuites: []uint16{ 24 | tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 25 | tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 26 | tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 27 | tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 28 | tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 29 | tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 30 | }, 31 | } 32 | srv := &http.Server{ 33 | Addr: serverAddress, 34 | ReadTimeout: 5 * time.Second, 35 | WriteTimeout: 10 * time.Second, 36 | IdleTimeout: 120 * time.Second, 37 | TLSConfig: tlsConfig, 38 | Handler: mux, 39 | } 40 | return srv 41 | } 42 | --------------------------------------------------------------------------------