dongmu3187 2017-11-23 06:08 采纳率: 0%
浏览 244
已采纳

返回响应后关闭HTTP服务器

I'm in the process of building a little command line based Go bot that interacts with the Instagram API.

The Instagram API is OAuth based, and so not overly great for command line based apps.

To get around this, I am opening the appropriate authorization URL in the browser and using a local server I spin up for the redirect URI - this way I can capture and gracefully show the access token as opposed to the user needing to get this from the URL manually.

So far so good, the application can successfully open the browser to the authorisation URL, you authorise it and it redirects you to the local HTTP server.

Now, I have no need for the HTTP server after the access token has been displayed to the user and so I am wanting to manually shut the server down after doing this.

To do this, I drew inspiration from this answer and drummed up the below:

package main

import (
    "fmt"
    "io"
    "log"
    "net/http"
    "os/exec"
    "runtime"
    "time"
)

var client_id = "my_client_id"
var client_secret = "my_client_secret"
var redirect_url = "http://localhost:8000/instagram/callback"

func main() {

    srv := startHttpServer()

    openbrowser(fmt.Sprintf("https://api.instagram.com/oauth/authorize/?client_id=%v&redirect_uri=%v&response_type=code", client_id, redirect_url))

    // Backup to gracefully shutdown the server
    time.Sleep(20 * time.Second)
    if err := srv.Shutdown(nil); err != nil {
        panic(err) // failure/timeout shutting down the server gracefully
    }
}

func showTokenToUser(w http.ResponseWriter, r *http.Request, srv *http.Server) {
    io.WriteString(w, fmt.Sprintf("Your access token is: %v", r.URL.Query().Get("code")))
    if err := srv.Shutdown(nil); err != nil {
        log.Fatal(err) // failure/timeout shutting down the server gracefully
    }
}

func startHttpServer() *http.Server {
    srv := &http.Server{Addr: ":8000"}

    http.HandleFunc("/instagram/callback", func(w http.ResponseWriter, r *http.Request) {
        showTokenToUser(w, r, srv)
    })

    go func() {
        if err := srv.ListenAndServe(); err != nil {
            // cannot panic, because this probably is an intentional close
            log.Printf("Httpserver: ListenAndServe() error: %s", err)
        }
    }()

    // returning reference so caller can call Shutdown()
    return srv
}

func openbrowser(url string) {
    var err error

    switch runtime.GOOS {
    case "linux":
        err = exec.Command("xdg-open", url).Start()
    case "windows":
        err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
    case "darwin":
        err = exec.Command("open", url).Start()
    default:
        err = fmt.Errorf("unsupported platform")
    }
    if err != nil {
        log.Fatal(err)
    }
}

However, the above causes this error:

2017/11/23 16:02:03 Httpserver: ListenAndServe() error: http: Server closed

2017/11/23 16:02:03 http: panic serving [::1]:61793: runtime error: invalid memory address or nil pointer dereference

If I comment out these lines in the handler then it works flawlessly, albeit without shutting down the server when I hit the callback route:

if err := srv.Shutdown(nil); err != nil {
    log.Fatal(err) // failure/timeout shutting down the server gracefully
}

Where am I going wrong? What do I need to change so that I can shut the server down when I hit the callback route, after displaying the text to the user.

  • 写回答

2条回答 默认 最新

  • dsavz66262 2017-11-23 06:55
    关注

    Use:

    ctx, cancel := context.WithCancel(context.Background())
    err := srv.Shutdown(ctx);
    

    Try this:

    package main
    
    import (
        "context"
        "io"
        "log"
        "net/http"
    )
    
    func main() {
        ctx, cancel := context.WithCancel(context.Background())
        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
            io.WriteString(w, "Hi
    ")
        })
        http.HandleFunc("/quit", func(w http.ResponseWriter, r *http.Request) {
            io.WriteString(w, "Bye
    ")
            cancel()
        })
        srv := &http.Server{Addr: ":8080"}
        go func() {
            if err := srv.ListenAndServe(); err != nil {
                log.Printf("Httpserver: ListenAndServe() error: %s", err)
            }
        }()
        <-ctx.Done()
        if err := srv.Shutdown(ctx); err != nil && err != context.Canceled {
            log.Println(err)
        }
        log.Println("done.")
    }
    

    And see: https://blog.golang.org/context

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 运筹学中在线排序的时间在线排序的在线LPT算法
  • ¥30 求一段fortran代码用IVF编译运行的结果
  • ¥15 深度学习根据CNN网络模型,搭建BP模型并训练MNIST数据集
  • ¥15 lammps拉伸应力应变曲线分析
  • ¥15 C++ 头文件/宏冲突问题解决
  • ¥15 用comsol模拟大气湍流通过底部加热(温度不同)的腔体
  • ¥50 安卓adb backup备份子用户应用数据失败
  • ¥20 有人能用聚类分析帮我分析一下文本内容嘛
  • ¥15 请问Lammps做复合材料拉伸模拟,应力应变曲线问题
  • ¥30 python代码,帮调试,帮帮忙吧