Go应用程序在NGINX代理后面无法使用:HTTP 502

时间:2019-01-06 20:24:16

标签: go nginx

我正在Go中创建Webhooks的接收器,这是我在Go中的第一个应用程序。

我已经在本地测试了该应用程序,并且可以在那儿工作。但是现在我已经将它部署在NGINX代理后面的Docker容器中的Ubuntu服务器上(该代理位于Docker外部)。 pingHandler有效,gitlabHandler可以发送403消息。但是,如果令牌有效,我将始终看到一条502消息,NGINX日志会告诉我:

*1115 upstream prematurely closed connection while reading response header from upstream, client: X.X.X.X, server: myserver.home.example, request: "POST /webhookreceiver/gitlab HTTP/1.1", upstream: "http://127.0.0.1:7080/gitlab", host: "myserver.home.example"

此外,当我发送无效的JSON有效负载时,它仍然会给出相同的错误消息,因此,如果我正确理解该错误,我的Go应用程序会在第72-76行之前关闭连接。我猜第65行有问题吗?

对于大多数遇到此问题的其他人来说,可以通过增加超时来解决此问题,因为他们的请求太大,但是在我的情况下,我正在使用仅几个字节的JSON消息进行测试。

main.go

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "strings"
    "time"
)

type ServerConfig struct {
    Https       bool   `json:"https"`
    Cert        string `json:"cert"`
    Key         string `json:"key"`
    Gitlabtoken string `json:"gitlab_token"`
}

type SplunkConfig struct {
    Token    string
    Url      string
    Metadata map[string]string
}

type SplunkEvent struct {
    Host       string                 `json:"host"`
    Sourcetype string                 `json:"sourcetype"`
    Index      string                 `json:"index"`
    Source     string                 `json:"source"`
    Event      map[string]interface{} `json:"event"`
}

var splunk_config SplunkConfig
var server_config ServerConfig

type middleware func(next http.HandlerFunc) http.HandlerFunc

func withLogging(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        log.Printf("IP %s", r.RemoteAddr)
        next.ServeHTTP(w, r)
    }
}

func chainMiddleware(mw ...middleware) middleware {
    return func(final http.HandlerFunc) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            last := final
            for i := len(mw) - 1; i >= 0; i-- {
                last = mw[i](last)
            }
            last(w, r)
        }
    }
}

func gitlabHandler(w http.ResponseWriter, req *http.Request) {
    // Check if GitLab token is present and correct
    gitlab_header := req.Header.Get("X-Gitlab-Token")
    if gitlab_header == server_config.Gitlabtoken {
        // Create SplunkEvent to send to Splunk
        body, err := ioutil.ReadAll(req.Body)
        if err != nil {
            log.Println(err)
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte("500 - Error reading body"))
            return
        }
        var event map[string]interface{}
        err = json.Unmarshal(body, &event)
        if err != nil {
            log.Println(err)
            w.WriteHeader(http.StatusBadRequest)
            w.Write([]byte("400 - Request cannot be parsed"))
            return
        }
        se := SplunkEvent{
            Host:       splunk_config.Metadata["host"],
            Sourcetype: splunk_config.Metadata["sourcetype"],
            Index:      splunk_config.Metadata["index"],
            Source:     "gitlab",
            Event:      event}
        j, err := json.Marshal(se)
        if err != nil {
            log.Println(err)
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte("500 - Error creating forwarding call"))
            return
        }
        // Send SplunkEvent to Splunk
        b := bytes.NewBuffer(j)
        client := &http.Client{
            Timeout: time.Second * 30,
        }
        req, err := http.NewRequest("POST", splunk_config.Url, b)
        if err != nil {
            log.Println(err)
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte("500 - Error creating request"))
            return
        }
        req.Header.Add("Authorization", "Splunk "+splunk_config.Token)
        resp, err := client.Do(req)
        if err != nil {
            log.Println(err)
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte("500 - Error sending request"))
            return
        }
        // Check response
        bod, err := ioutil.ReadAll(resp.Body)
        if strings.Contains(string(bod), "Success") {
            log.Println("Received and succesfully processed request")
            w.WriteHeader(http.StatusOK)
            w.Write([]byte("200 - OK"))
            return
        } else {
            log.Println(string(bod))
            w.WriteHeader(http.StatusInternalServerError)
            w.Write([]byte("500 - Error sending to Splunk"))
            return
        }
        defer resp.Body.Close()
    } else {
        log.Println("Incorrect Gitlab token")
        w.WriteHeader(http.StatusForbidden)
        w.Write([]byte("403 - Forbidden"))
        return
    }
}

func pingHandler(w http.ResponseWriter, r *http.Request) {
    log.Println("Received ping call")
    fmt.Fprint(w, "{\"check\": \"online\"}")
}

func main() {
    // Setup logging
    file, err := os.OpenFile("webhookreceiver.log", os.O_CREATE|os.O_APPEND, 0644)
    if err != nil {
        log.Fatal("Error opening log file: " + err.Error())
    }
    defer file.Close()
    log.SetOutput(file)
    // Load Splunk config
    data, err := ioutil.ReadFile("configs/splunk.json")
    if err != nil {
        log.Fatal("Error reading splunk_config.json: " + err.Error())
    }
    err = json.Unmarshal(data, &splunk_config)
    if err != nil {
        log.Fatal("Error on unmarshal of Splunk config: " + err.Error())
    }
    // Load server config
    data, err = ioutil.ReadFile("configs/server.json")
    if err != nil {
        log.Fatal("Error reading server_config.json: " + err.Error())
    }
    err = json.Unmarshal(data, &server_config)
    if err != nil {
        log.Fatal("Error on unmarshal of Server config: " + err.Error())
    }
    // Start server
    log.Println("Starting server")
    mw := chainMiddleware(withLogging)
    http.Handle("/gitlab", mw(gitlabHandler))
    http.HandleFunc("/ping", mw(pingHandler))
    if server_config.Https {
        log.Fatal(http.ListenAndServeTLS(":7443", server_config.Cert, server_config.Key, nil))
    } else {
        log.Fatal(http.ListenAndServe(":7080", nil))
    }
}

NGINX配置的相关位

server {
  listen 443 ssl;
  listen [::]:443 ssl;
  ssl_certificate /blabla/fullchain.pem;
  ssl_certificate_key /blabla/privkey.pem;
  client_max_body_size 3M;
  add_header Strict-Transport-Security "max-age=31536000" always;
  location /webhookreceiver/ {
    proxy_pass http://127.0.0.1:7080/;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

我的Go应用程序的日志记录也在本地运行,但不在Docker容器内部运行,正在创建日志文件,但它保持为空。所以在这一点上,我没有任何消息,除非有人知道为什么;-)

1 个答案:

答案 0 :(得分:0)

您是否尝试查看Docker日志而不是无效的日志文件?首先获取容器ID:

docker container ls

然后获取日志:

docker logs 14eb7d0a2332

nginx错误意味着您的Golang应用程序接受了该连接,但是关闭了该连接而没有返回响应。这表明您的Webhook正在返回而没有编写响应。

请求成功后,您的gitlabHandler似乎没有返回响应,您要做的最后一件事是读取上游响应:

    bod, err := ioutil.ReadAll(resp.Body)
    if strings.Contains(string(bod), "Success") {
        log.Println("Received and succesfully processed request")
    } else {
        log.Println(string(bod))
    }
    defer resp.Body.Close()

您需要在此处写入ResponseWriter。