From 3fe42e1ff06d65fb179e3937b5a187c1671bf47c Mon Sep 17 00:00:00 2001 From: Paul Brinkmeier Date: Wed, 17 Jul 2024 07:35:11 +0200 Subject: [PATCH] Timeout after 10 seconds on FetchDepartures Also apply gofmt --- README.md | 2 +- main.go | 200 +++++++++++++++++++++++++++--------------------------- 2 files changed, 102 insertions(+), 100 deletions(-) diff --git a/README.md b/README.md index ade0e67..021b6d1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## TODO -- [ ] Use timeout for fetching departures +- [x] Use timeout for fetching departures - [ ] Write ESP8266 client - [ ] Add basic auth - [ ] Create Nix package diff --git a/main.go b/main.go index c134b64..edb887c 100644 --- a/main.go +++ b/main.go @@ -7,134 +7,136 @@ import "net/http" import "net/url" import "slices" import "strconv" +import "time" type DMResponse struct { - Departures []DMDeparture `json:"departureList"` + Departures []DMDeparture `json:"departureList"` } type DMDeparture struct { - StopName string `json:"stopName"` - Platform string `json:"platform"` - Countdown string `json:"countdown"` - DateTime DMDateTime `json:"dateTime"` - RealDateTime *DMDateTime `json:"realDateTime"` - ServingLine DMServingLine `json:"servingLine"` - OnwardStopSeq []DMStop `json:"onwardStopSeq"` + StopName string `json:"stopName"` + Platform string `json:"platform"` + Countdown string `json:"countdown"` + DateTime DMDateTime `json:"dateTime"` + RealDateTime *DMDateTime `json:"realDateTime"` + ServingLine DMServingLine `json:"servingLine"` + OnwardStopSeq []DMStop `json:"onwardStopSeq"` } type DMServingLine struct { - Symbol string `json:"symbol"` - Direction string `json:"direction"` + Symbol string `json:"symbol"` + Direction string `json:"direction"` } type DMStop struct { - PlaceID string `json:"placeID"` - Place string `json:"place"` - NameWO string `json:"nameWO"` + PlaceID string `json:"placeID"` + Place string `json:"place"` + NameWO string `json:"nameWO"` } type DMDateTime struct { - Hour string `json:"hour"` - Minute string `json:"minute"` + Hour string `json:"hour"` + Minute string `json:"minute"` } -// TODO: Use different client (with timeout) func FetchDepartures(stopId string) (*DMResponse, error) { - // Create request object - req, err := http.NewRequest("GET", "https://www.vrn.de/mngvrn/XML_DM_REQUEST", nil) - if err != nil { - return nil, err - } + // Create request object + req, err := http.NewRequest("GET", "https://www.vrn.de/mngvrn/XML_DM_REQUEST", nil) + if err != nil { + return nil, err + } - // Configure our request - query := url.Values{} - query.Set("coordOutputFormat", "EPSG:4326") - query.Set("depType", "stopEvents") - query.Set("includeCompleteStopSeq", "1") - query.Set("limit", "10") - query.Set("locationServerActive", "0") - query.Set("mode", "direct") - query.Set("name_dm", stopId) - query.Set("outputFormat", "json") - query.Set("type_dm", "stop") - query.Set("useOnlyStops", "1") - query.Set("useRealtime", "1") - req.URL.RawQuery = query.Encode() + // Configure our request + query := url.Values{} + query.Set("coordOutputFormat", "EPSG:4326") + query.Set("depType", "stopEvents") + query.Set("includeCompleteStopSeq", "1") + query.Set("limit", "10") + query.Set("locationServerActive", "0") + query.Set("mode", "direct") + query.Set("name_dm", stopId) + query.Set("outputFormat", "json") + query.Set("type_dm", "stop") + query.Set("useOnlyStops", "1") + query.Set("useRealtime", "1") + req.URL.RawQuery = query.Encode() - // Send the request - res, err := http.DefaultClient.Do(req) - if err != nil { - return nil, err - } + // Send the request, wait max 10 seconds + client := http.Client{ + Timeout: 10 * time.Second, + } + res, err := client.Do(req) + if err != nil { + return nil, err + } - defer res.Body.Close() - var dmResponse DMResponse - err = json.NewDecoder(res.Body).Decode(&dmResponse) - if err != nil { - return nil, err - } + defer res.Body.Close() + var dmResponse DMResponse + err = json.NewDecoder(res.Body).Decode(&dmResponse) + if err != nil { + return nil, err + } - return &dmResponse, nil + return &dmResponse, nil } func main() { - http.HandleFunc("/departures", func(w http.ResponseWriter, r *http.Request) { - query := r.URL.Query() + http.HandleFunc("/departures", func(w http.ResponseWriter, r *http.Request) { + query := r.URL.Query() - if query["stop_id"] == nil || len(query["stop_id"]) < 1 { - http.Error(w, "Missing query parameter: stop_id", http.StatusBadRequest) - return - } - stopId := query["stop_id"][0] + if query["stop_id"] == nil || len(query["stop_id"]) < 1 { + http.Error(w, "Missing query parameter: stop_id", http.StatusBadRequest) + return + } + stopId := query["stop_id"][0] - var platform *string = nil - if query["platform"] != nil && len(query["platform"]) != 0 { - platform = &query["platform"][0] - } - + var platform *string = nil + if query["platform"] != nil && len(query["platform"]) != 0 { + platform = &query["platform"][0] + } - ds, err := FetchDepartures(stopId) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } + ds, err := FetchDepartures(stopId) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } - for _, d := range ds.Departures { - if platform != nil { - if d.Platform != *platform { - continue - } - } + for _, d := range ds.Departures { + if platform != nil { + if d.Platform != *platform { + continue + } + } - direction := d.ServingLine.Direction - if len(d.OnwardStopSeq) != 0 { - last := d.OnwardStopSeq[len(d.OnwardStopSeq) - 1] - if slices.Contains([]string{"5", "6"}, last.PlaceID) { - direction = last.NameWO - } - } + direction := d.ServingLine.Direction + if len(d.OnwardStopSeq) != 0 { + last := d.OnwardStopSeq[len(d.OnwardStopSeq)-1] + if slices.Contains([]string{"5", "6"}, last.PlaceID) { + direction = last.NameWO + } + } - leaving := fmt.Sprintf("%s min", d.Countdown) - countdown, err := strconv.Atoi(d.Countdown) - if err == nil { - if countdown == 0 { - leaving = "sofort" - } - if countdown > 20 { - dt := d.DateTime - if d.RealDateTime != nil { - dt = *d.RealDateTime - } - leaving = fmt.Sprintf("%02s:%02s", dt.Hour, dt.Minute) - } - } + leaving := fmt.Sprintf("%s min", d.Countdown) + countdown, err := strconv.Atoi(d.Countdown) + if err == nil { + if countdown == 0 { + leaving = "sofort" + } + if countdown > 20 { + dt := d.DateTime + if d.RealDateTime != nil { + dt = *d.RealDateTime + } + leaving = fmt.Sprintf("%02s:%02s", dt.Hour, dt.Minute) + } + } - fmt.Fprintf(w, "%2s %-22s %6s\n", - d.ServingLine.Symbol, - direction[:min(len(direction), 22)], - leaving, - ) - } - }) - log.Fatal(http.ListenAndServe(":8000", nil)) + fmt.Fprintf(w, "%2s %-22s %6s\n", + d.ServingLine.Symbol, + direction[:min(len(direction), 22)], + leaving, + ) + } + }) + log.Fatal(http.ListenAndServe(":8000", nil)) }