dongsiju1941 2019-01-06 20:24
浏览 193

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

I am creating a receiver for webhooks in Go, this is my first application in Go.

I have tested the application locally and it works over there. But now I have deployed it on my Ubuntu server in a Docker container behind an NGINX proxy (the proxy is outside of Docker). The pingHandler works, and the gitlabHandler can send the 403 message. But if the token is valid I will always see a 502 message and the NGINX log tells me:

*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"

Also when I send an invalid JSON payload it will still give the same error message, so if I understand the error correctly my Go application closes the connection somewhere before line 72-76. I am guessing something is wrong with line 65?

For most other people having this issue it can be solved by increasing the timeout because their requests are too large, but in my case I am testing with a JSON message of only a few bytes.

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))
    }
}

Relevant bit of NGINX config

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;
  }
}

Also the logging of my Go application is working locally but not inside of my Docker container, the log file is being created but it stays empty. So at this point I don't have any messages from there, unless someone knows why that is ;-)

  • 写回答

1条回答 默认 最新

  • dqhnp44220 2019-01-06 20:29
    关注

    Have you tried looking at the Docker logs instead of the log file that isn't working? First get the container ID:

    docker container ls
    

    Then get the log:

    docker logs 14eb7d0a2332
    

    The nginx error means that your Golang application accepted the connection, but closed the connection without returning a response. This indicates that your webhook is returning without writing a response.

    It looks like your gitlabHandler does not return a response when the request succeeds, the last thing that you do is read the upstream response:

        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()
    

    You need to write to the ResponseWriter here.

    评论

报告相同问题?

悬赏问题

  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?
  • ¥15 求daily translation(DT)偏差订正方法的代码
  • ¥15 js调用html页面需要隐藏某个按钮
  • ¥15 ads仿真结果在圆图上是怎么读数的
  • ¥20 Cotex M3的调试和程序执行方式是什么样的?
  • ¥20 java项目连接sqlserver时报ssl相关错误
  • ¥15 一道python难题3