Go 应用程序在 NGINX 代理 : HTTP 502 后面不工作

标签 go nginx

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

我已经在本地测试了该应用程序,它在那里可以正常工作。但现在我已经将它部署在我的 Ubuntu 服务器上,位于 NGINX 代理后面的 Docker 容器中(代理在 Docker 之外)。 pingHandler 起作用,gitlabHandler 可以发送 403 消息。但如果 token 有效,我将始终看到 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 消息。

ma​​in.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 容器内不工作,正在创建日志文件,但它保持为空。所以此时我没有收到任何消息,除非有人知道这是为什么 ;-)

最佳答案

您是否尝试过查看 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。

关于Go 应用程序在 NGINX 代理 : HTTP 502 后面不工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54065606/

相关文章:

ssl - 通过客户端证书验证保护特定的 nginx-ingress 位置

go - 如何覆盖 Go 中的符号链接(symbolic link)?

windows - 编译Go 1.4.2 报错"dereferencing type-punned pointer"

file - 在 Go 中使用相同的 *os.File 写入和读取文件

string - 比较两个大文本文件的最有效方法

nginx - 无法在 Centos 上启动 NGINX

nginx - 如何使用配置映射在容器内创建单个文件?

regex - 前瞻正则表达式 - 识别重复的连续字母

nginx - 如何直接使用 NGINX 提供所有现有静态文件,并将其余文件代理到后端服务器。

node.js - 无法在 Swift 5 中调用 https 请求(自签名证书)