jjzjj

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

coder 2024-07-09 原文

我正在为 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/

有关Go 应用程序在 NGINX 代理 : HTTP 502 后面不工作的更多相关文章

  1. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  2. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  3. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  4. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  5. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  6. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  7. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  8. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  9. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

    我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

  10. ruby-on-rails - rspec should have_select ('cars' , :options => ['volvo' , 'saab' ] 不工作 - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion在首页我有:汽车:VolvoSaabMercedesAudistatic_pages_spec.rb中的测试代码:it"shouldhavetherightselect"dovisithome_pathit{shouldhave_select('cars',:options=>['volvo','saab','mercedes','audi'])}end响应是rspec./spec/request

随机推荐