router.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. // SPDX-FileCopyrightText: Adam Evyčędo
  2. //
  3. // SPDX-License-Identifier: AGPL-3.0-or-later
  4. package server
  5. import (
  6. "apiote.xyz/p/szczanieckiej/api"
  7. "apiote.xyz/p/szczanieckiej/config"
  8. "apiote.xyz/p/szczanieckiej/traffic"
  9. traffic_errors "apiote.xyz/p/szczanieckiej/traffic/errors"
  10. "errors"
  11. "fmt"
  12. "io"
  13. "log"
  14. "net/http"
  15. "os"
  16. "strconv"
  17. "strings"
  18. "golang.org/x/text/language"
  19. "git.sr.ht/~sircmpwn/go-bare"
  20. )
  21. type ServerError struct {
  22. code int
  23. field string
  24. value string
  25. err error
  26. }
  27. func (e ServerError) Error() string {
  28. message := ""
  29. switch e.code {
  30. case http.StatusBadRequest:
  31. message = e.value + " not valid as " + e.field
  32. case http.StatusNotFound:
  33. message = e.value + " not found as " + e.field
  34. default:
  35. message = fmt.Sprintf("error %d", e.code)
  36. if e.field != "" {
  37. message += " in field " + e.field
  38. }
  39. if e.value != "" {
  40. message += " with value " + e.value
  41. }
  42. if e.err != nil {
  43. message += ": " + e.err.Error()
  44. }
  45. }
  46. return message
  47. }
  48. func handleFeed(w http.ResponseWriter, r *http.Request, feedID string, cfg config.Config, t *traffic.Traffic, accept map[uint]struct{}) error {
  49. if _, ok := accept[4]; !ok {
  50. return ServerError{
  51. code: http.StatusNotAcceptable,
  52. }
  53. }
  54. timetable, err := traffic.GetTimetable(feedID, t, cfg)
  55. defer timetable.Close()
  56. if err != nil {
  57. return fmt.Errorf("while getting timetable: %w", err)
  58. }
  59. _, err = io.Copy(w, timetable)
  60. if err != nil {
  61. return fmt.Errorf("while writing: %w", err)
  62. }
  63. return nil
  64. }
  65. func handleLocatables(w http.ResponseWriter, r *http.Request, feedNames []string, cfg config.Config, t *traffic.Traffic, accept map[uint]struct{}) error {
  66. var locatablesSuccess api.LocatablesResponse
  67. if _, ok := accept[0]; ok {
  68. locatablesSuccess = api.LocatablesResponseDev{}
  69. } else if _, ok := accept[3]; ok {
  70. locatablesSuccess = api.LocatablesResponseV3{}
  71. } else if _, ok := accept[2]; ok {
  72. locatablesSuccess = api.LocatablesResponseV2{}
  73. } else if _, ok := accept[1]; ok {
  74. locatablesSuccess = api.LocatablesResponseV1{}
  75. } else {
  76. return ServerError{
  77. code: http.StatusNotAcceptable,
  78. }
  79. }
  80. err := r.ParseForm()
  81. if err != nil {
  82. return fmt.Errorf("while parsing form: %w", err)
  83. }
  84. dateString := r.Form.Get("date")
  85. lb, err := traffic.ParsePosition(r.Form.Get("lb"))
  86. if err != nil {
  87. return ServerError{
  88. code: http.StatusBadRequest,
  89. field: "lb",
  90. value: r.Form.Get("lb"),
  91. err: err,
  92. }
  93. }
  94. rt, err := traffic.ParsePosition(r.Form.Get("rt"))
  95. if err != nil {
  96. return ServerError{
  97. code: http.StatusBadRequest,
  98. field: "rt",
  99. value: r.Form.Get("rt"),
  100. err: err,
  101. }
  102. }
  103. for _, feedName := range feedNames {
  104. versionCode, _, err := parseDate(dateString, feedName, t)
  105. if err != nil {
  106. return fmt.Errorf("while parsing date: %w", err)
  107. }
  108. context := traffic.Context{
  109. DataHome: cfg.FeedsPath,
  110. FeedID: feedName,
  111. Version: versionCode,
  112. }
  113. stops, err := traffic.GetStopsIn(lb, rt, context, t)
  114. if err != nil {
  115. return fmt.Errorf("while getting stops in bounding box: %w", err)
  116. }
  117. vehicles, err := traffic.GetVehiclesIn(lb, rt, context, t)
  118. if err != nil {
  119. return fmt.Errorf("while getting vehicles in bounding box: %w", err)
  120. }
  121. locatables := []traffic.Locatable{}
  122. for _, stop := range stops {
  123. locatables = append(locatables, stop)
  124. }
  125. for _, vehicle := range vehicles {
  126. locatables = append(locatables, vehicle)
  127. }
  128. if _, ok := accept[0]; ok {
  129. locatablesSuccess, err = api.CreateSuccessLocatablesV3(locatables, context, t, locatablesSuccess)
  130. } else if _, ok := accept[3]; ok {
  131. locatablesSuccess, err = api.CreateSuccessLocatablesV3(locatables, context, t, locatablesSuccess)
  132. } else if _, ok := accept[2]; ok {
  133. locatablesSuccess, err = api.CreateSuccessLocatablesV2(locatables, context, t, locatablesSuccess)
  134. } else if _, ok := accept[1]; ok {
  135. locatablesSuccess, err = api.CreateSuccessLocatables(locatables, context, t, locatablesSuccess)
  136. } else {
  137. return ServerError{
  138. code: http.StatusNotAcceptable,
  139. }
  140. }
  141. if err != nil {
  142. return fmt.Errorf("while creating locatablesSuccess from near locatables: %w", err)
  143. }
  144. }
  145. bytes, err := bare.Marshal(&locatablesSuccess)
  146. if err != nil {
  147. return fmt.Errorf("while marshaling: %w", err)
  148. }
  149. _, err = w.Write(bytes)
  150. if err != nil {
  151. return fmt.Errorf("while writing: %w", err)
  152. }
  153. return nil
  154. }
  155. func handleQueryables(w http.ResponseWriter, r *http.Request, feedNames []string, cfg config.Config, t *traffic.Traffic, accept map[uint]struct{}) error {
  156. var queryablesSuccess api.QueryablesResponse
  157. if _, ok := accept[0]; ok {
  158. queryablesSuccess = api.QueryablesResponseDev{}
  159. } else if _, ok := accept[4]; ok {
  160. queryablesSuccess = api.QueryablesResponseV4{}
  161. } else if _, ok := accept[3]; ok {
  162. queryablesSuccess = api.QueryablesResponseV3{}
  163. } else if _, ok := accept[2]; ok {
  164. queryablesSuccess = api.QueryablesResponseV2{}
  165. } else if _, ok := accept[1]; ok {
  166. queryablesSuccess = api.QueryablesResponseV1{}
  167. } else {
  168. return ServerError{
  169. code: http.StatusNotAcceptable,
  170. }
  171. }
  172. err := r.ParseForm()
  173. if err != nil {
  174. return fmt.Errorf("while parsing form: %w", err)
  175. }
  176. query := r.Form.Get("q")
  177. near := r.Form.Get("near")
  178. dateString := r.Form.Get("date")
  179. limitString := r.Form.Get("limit")
  180. if limitString == "" {
  181. limitString = "12"
  182. }
  183. limit, err := strconv.ParseUint(limitString, 10, 0)
  184. if err != nil {
  185. return ServerError{
  186. code: http.StatusBadRequest,
  187. field: "limit",
  188. value: limitString,
  189. }
  190. }
  191. offsetString := r.Form.Get("offset")
  192. if offsetString == "" {
  193. offsetString = "0"
  194. }
  195. offset, err := strconv.ParseUint(offsetString, 10, 0)
  196. if err != nil {
  197. return ServerError{
  198. code: http.StatusBadRequest,
  199. field: "offset",
  200. value: offsetString,
  201. }
  202. }
  203. for _, feedName := range feedNames {
  204. versionCode, _, err := parseDate(dateString, feedName, t)
  205. if err != nil {
  206. return fmt.Errorf("while parsing date: %w", err)
  207. }
  208. context := traffic.Context{
  209. DataHome: cfg.FeedsPath,
  210. FeedID: feedName,
  211. Version: versionCode,
  212. }
  213. if near != "" {
  214. location, err := traffic.ParsePosition(near)
  215. if err != nil {
  216. return ServerError{
  217. code: http.StatusBadRequest,
  218. field: "near",
  219. value: near,
  220. err: err,
  221. }
  222. }
  223. stops, err := traffic.GetStopsNear(location, context, t)
  224. if err != nil {
  225. return fmt.Errorf("while getting near stops: %w", err)
  226. }
  227. items := []traffic.Queryable{}
  228. for _, stop := range stops {
  229. items = append(items, stop)
  230. }
  231. if _, ok := accept[0]; ok {
  232. queryablesSuccess, err = api.CreateSuccessQueryablesV4(near, items, context, t, queryablesSuccess, true)
  233. } else if _, ok := accept[4]; ok {
  234. queryablesSuccess, err = api.CreateSuccessQueryablesV4(near, items, context, t, queryablesSuccess, true)
  235. } else if _, ok := accept[3]; ok {
  236. queryablesSuccess, err = api.CreateSuccessQueryablesV3(near, items, context, t, queryablesSuccess, true)
  237. } else if _, ok := accept[2]; ok {
  238. queryablesSuccess, err = api.CreateSuccessQueryablesV2(near, items, context, t, queryablesSuccess, true)
  239. } else if _, ok := accept[1]; ok {
  240. queryablesSuccess, err = api.CreateSuccessQueryables(items, context, t, queryablesSuccess, true)
  241. } else {
  242. return ServerError{
  243. code: http.StatusNotAcceptable,
  244. }
  245. }
  246. if err != nil {
  247. return fmt.Errorf("while creating stopsSuccess from near stops: %w", err)
  248. }
  249. } else {
  250. ix := t.CodeIndexes[feedName][versionCode]
  251. code := query
  252. _, exists := ix[code]
  253. if exists {
  254. stop, err := traffic.GetStop(code, context, t)
  255. if err != nil {
  256. return fmt.Errorf("while getting stop: %w", err)
  257. }
  258. if _, ok := accept[0]; ok {
  259. queryablesSuccess, err = api.CreateSuccessQueryablesV4(query, []traffic.Queryable{stop}, context, t, queryablesSuccess, false)
  260. } else if _, ok := accept[4]; ok {
  261. queryablesSuccess, err = api.CreateSuccessQueryablesV4(query, []traffic.Queryable{stop}, context, t, queryablesSuccess, false)
  262. } else if _, ok := accept[3]; ok {
  263. queryablesSuccess, err = api.CreateSuccessQueryablesV3(query, []traffic.Queryable{stop}, context, t, queryablesSuccess, false)
  264. } else if _, ok := accept[2]; ok {
  265. queryablesSuccess, err = api.CreateSuccessQueryablesV2(query, []traffic.Queryable{stop}, context, t, queryablesSuccess, false)
  266. } else if _, ok := accept[1]; ok {
  267. queryablesSuccess, err = api.CreateSuccessQueryables([]traffic.Queryable{stop}, context, t, queryablesSuccess, false)
  268. } else {
  269. return ServerError{
  270. code: http.StatusNotAcceptable,
  271. }
  272. }
  273. if err != nil {
  274. return fmt.Errorf("while creating stopsSuccess from code: %w", err)
  275. }
  276. } else {
  277. query, err = traffic.CleanQuery(query, t.Feeds[feedName])
  278. if err != nil {
  279. return fmt.Errorf("while cleaning query: %w", err)
  280. }
  281. lines, err1 := traffic.QueryLines(query, cfg.FeedsPath, feedName, versionCode, t)
  282. stops, err2 := traffic.QueryStops(query, context, t)
  283. if err1 != nil && err2 != nil {
  284. return fmt.Errorf("while querying stops and lines: %w", errors.Join(err1, err2))
  285. }
  286. items := []traffic.Queryable{}
  287. for _, line := range lines {
  288. items = append(items, line)
  289. }
  290. for _, stop := range stops {
  291. items = append(items, stop)
  292. }
  293. if _, ok := accept[0]; ok {
  294. queryablesSuccess, err = api.CreateSuccessQueryablesV4(query, items, context, t, queryablesSuccess, false)
  295. } else if _, ok := accept[4]; ok {
  296. queryablesSuccess, err = api.CreateSuccessQueryablesV4(query, items, context, t, queryablesSuccess, false)
  297. } else if _, ok := accept[3]; ok {
  298. queryablesSuccess, err = api.CreateSuccessQueryablesV3(query, items, context, t, queryablesSuccess, false)
  299. } else if _, ok := accept[2]; ok {
  300. queryablesSuccess, err = api.CreateSuccessQueryablesV2(query, items, context, t, queryablesSuccess, false)
  301. } else if _, ok := accept[1]; ok {
  302. queryablesSuccess, err = api.CreateSuccessQueryables(items, context, t, queryablesSuccess, false)
  303. } else {
  304. return ServerError{
  305. code: http.StatusNotAcceptable,
  306. }
  307. }
  308. if err != nil {
  309. return fmt.Errorf("while creating stopsSuccess from lines and stops: %w", err)
  310. }
  311. }
  312. }
  313. }
  314. queryablesSuccess = api.LimitQueryables(queryablesSuccess, offset, limit)
  315. bytes, err := bare.Marshal(&queryablesSuccess)
  316. if err != nil {
  317. return fmt.Errorf("while marshaling: %w", err)
  318. }
  319. _, err = w.Write(bytes)
  320. if err != nil {
  321. return fmt.Errorf("while writing: %w", err)
  322. }
  323. return nil
  324. }
  325. func handleDepartures(w http.ResponseWriter, r *http.Request, feedName string, cfg config.Config, t *traffic.Traffic, accept map[uint]struct{}) error {
  326. err := r.ParseForm()
  327. if err != nil {
  328. return fmt.Errorf("while parsing form: %w", err)
  329. }
  330. code := r.Form.Get("code")
  331. if code == "" {
  332. return ServerError{
  333. code: http.StatusBadRequest,
  334. field: "code",
  335. value: "EMPTY",
  336. }
  337. }
  338. dateString := r.Form.Get("date")
  339. line := r.Form.Get("line")
  340. lineID := r.Form.Get("lineID")
  341. limitString := r.Form.Get("limit")
  342. if limitString == "" {
  343. limitString = "12"
  344. }
  345. limit, err := strconv.ParseUint(limitString, 10, 0)
  346. if err != nil {
  347. return ServerError{
  348. code: http.StatusBadRequest,
  349. field: "limit",
  350. value: limitString,
  351. }
  352. }
  353. offsetString := r.Form.Get("offset")
  354. if offsetString == "" {
  355. offsetString = "0"
  356. }
  357. offset, err := strconv.ParseUint(offsetString, 10, 0)
  358. if err != nil {
  359. return ServerError{
  360. code: http.StatusBadRequest,
  361. field: "offset",
  362. value: offsetString,
  363. }
  364. }
  365. departuresType := traffic.DEPARTURES_FULL
  366. if dateString == "" {
  367. departuresType = traffic.DEPARTURES_HYBRID
  368. }
  369. versionCode, date, err := parseDate(dateString, feedName, t)
  370. if err != nil {
  371. return err
  372. }
  373. context := traffic.Context{
  374. DataHome: cfg.FeedsPath,
  375. FeedID: feedName,
  376. Version: versionCode,
  377. }
  378. ix := t.CodeIndexes[feedName][versionCode]
  379. _, exists := ix[code]
  380. if !exists {
  381. return ServerError{
  382. code: http.StatusNotFound,
  383. field: "code",
  384. value: string(code),
  385. }
  386. }
  387. stop, err := traffic.GetStop(code, context, t)
  388. if err != nil {
  389. return fmt.Errorf("while getting stop: %w", err)
  390. }
  391. if lineID == "" {
  392. l, _ := traffic.GetLineOld(line, context, t)
  393. lineID = l.Id
  394. }
  395. acceptLanguage := r.Header.Get("Accept-Language")
  396. if acceptLanguage == "" {
  397. acceptLanguage, err = traffic.GetLanguage(context)
  398. if err != nil {
  399. log.Printf("while gettng default language: %v\n", err)
  400. acceptLanguage = "und"
  401. }
  402. }
  403. preferredLanguages, _, err := language.ParseAcceptLanguage(acceptLanguage)
  404. if err != nil {
  405. return ServerError{
  406. code: http.StatusBadRequest,
  407. field: "Accept-Language",
  408. value: acceptLanguage,
  409. err: err,
  410. }
  411. }
  412. departures, err := traffic.GetDepartures(code, lineID, context, t, date, departuresType, preferredLanguages)
  413. if err != nil {
  414. if _, ok := err.(traffic_errors.NoSchedule); ok {
  415. return ServerError{
  416. code: http.StatusNotFound,
  417. field: "date",
  418. value: dateString,
  419. }
  420. } else {
  421. return fmt.Errorf("while getting departures: %w", err)
  422. }
  423. }
  424. if departuresType == traffic.DEPARTURES_HYBRID {
  425. if int(offset) > len(departures) {
  426. departures = []traffic.DepartureRealtime{}
  427. } else if len(departures) < int(offset+limit) {
  428. departures = departures[offset:]
  429. } else {
  430. departures = departures[offset : offset+limit]
  431. }
  432. }
  433. stopAlerts := traffic.GetAlerts(stop.Id, stop.Code, -1, context, t, preferredLanguages)
  434. if err != nil {
  435. return fmt.Errorf("while getting alerts: %w", err)
  436. }
  437. var success api.DeparturesResponse
  438. if _, ok := accept[0]; ok {
  439. success, err = api.CreateSuccessDeparturesDev(stop, departures, date, t.Vehicles[feedName][versionCode], stopAlerts, context, t, accept, preferredLanguages)
  440. } else if _, ok := accept[4]; ok {
  441. success, err = api.CreateSuccessDeparturesV4(stop, departures, date, t.Vehicles[feedName][versionCode], stopAlerts, context, t, accept, preferredLanguages)
  442. } else if _, ok := accept[3]; ok {
  443. success, err = api.CreateSuccessDeparturesV3(stop, departures, date, t.Vehicles[feedName][versionCode], stopAlerts, context, t, accept, preferredLanguages)
  444. } else if _, ok := accept[2]; ok {
  445. success, err = api.CreateSuccessDeparturesV2(stop, departures, date, t.Vehicles[feedName][versionCode], stopAlerts, context, t, accept, preferredLanguages)
  446. } else if _, ok := accept[1]; ok {
  447. success, err = api.CreateSuccessDeparturesV1(stop, departures, date, t.Vehicles[feedName][versionCode], stopAlerts, context, t, accept, preferredLanguages)
  448. } else {
  449. return ServerError{
  450. code: http.StatusNotAcceptable,
  451. }
  452. }
  453. if err != nil {
  454. return fmt.Errorf("while creating departuresSuccess: %w", err)
  455. }
  456. bytes, err := bare.Marshal(&success)
  457. if err != nil {
  458. return fmt.Errorf("while marshaling: %w", err)
  459. }
  460. _, err = w.Write(bytes)
  461. if err != nil {
  462. return fmt.Errorf("while writing: %w", err)
  463. }
  464. return nil
  465. }
  466. func handleTrip(w http.ResponseWriter, r *http.Request, feedName string, cfg config.Config, t *traffic.Traffic, accept uint) error {
  467. path := strings.Split(r.URL.Path[1:], "/")
  468. if len(path) == 3 {
  469. dateString := r.Form.Get("date")
  470. versionCode, _, err := parseDate(dateString, feedName, t)
  471. if err != nil {
  472. return err
  473. }
  474. tripID := path[2]
  475. stopCode := r.Form.Get("stop")
  476. context := traffic.Context{
  477. DataHome: cfg.FeedsPath,
  478. FeedID: feedName,
  479. Version: versionCode,
  480. }
  481. trip, err := traffic.GetTripFromStop(tripID, stopCode, context, t)
  482. if err != nil {
  483. return fmt.Errorf("while getting line: %w", err)
  484. }
  485. if len(trip) == 0 {
  486. return ServerError{
  487. code: http.StatusNotFound,
  488. field: "line",
  489. value: tripID,
  490. }
  491. }
  492. success := struct{}{} //api.CreateSuccessTrip(trip)
  493. bytes, err := bare.Marshal(&success)
  494. if err != nil {
  495. return fmt.Errorf("while marshaling trip: %w", err)
  496. }
  497. _, err = w.Write(bytes)
  498. if err != nil {
  499. return fmt.Errorf("while writing: %w", err)
  500. }
  501. } else {
  502. return ServerError{
  503. code: http.StatusNotFound,
  504. field: "trip",
  505. value: "EMPTY",
  506. }
  507. }
  508. return nil
  509. }
  510. func sendError(w http.ResponseWriter, r *http.Request, err error) {
  511. var (
  512. se ServerError
  513. response api.ErrorResponse
  514. )
  515. if !errors.As(err, &se) {
  516. se = ServerError{
  517. code: http.StatusInternalServerError,
  518. err: err,
  519. }
  520. }
  521. response = api.ErrorResponse{
  522. Field: se.field,
  523. Message: se.Error(),
  524. }
  525. log.Println(err.Error())
  526. b, err := bare.Marshal(&response)
  527. if err != nil {
  528. w.WriteHeader(http.StatusInternalServerError)
  529. return
  530. }
  531. w.WriteHeader(se.code)
  532. w.Write(b)
  533. }
  534. func Route(cfg config.Config, traffic *traffic.Traffic) *http.Server {
  535. srv := &http.Server{Addr: cfg.ListenAddress}
  536. http.DefaultServeMux = &http.ServeMux{}
  537. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  538. log.Printf("%s %s?%s\n", r.Method, r.URL.Path, r.URL.RawQuery)
  539. accept, err := parseAccept(r.Header.Values("Accept"))
  540. if err != nil {
  541. sendError(w, r, fmt.Errorf("while parsing accept: %w", err))
  542. return
  543. }
  544. if r.URL.Path[1:] == "" {
  545. err = handleFeeds(w, r, cfg, traffic, accept)
  546. } else {
  547. path := strings.Split(r.URL.Path[1:], "/")
  548. feedNames := strings.Split(path[0], ",")
  549. for _, feedName := range feedNames {
  550. if traffic.Versions[feedName] == nil {
  551. sendError(w, r, ServerError{
  552. code: http.StatusNotFound,
  553. field: "feed",
  554. value: feedName,
  555. })
  556. return
  557. }
  558. }
  559. if len(path) == 1 {
  560. if len(feedNames) > 1 {
  561. err = ServerError{
  562. code: http.StatusBadRequest,
  563. field: "feed",
  564. value: path[0],
  565. }
  566. } else {
  567. err = handleFeed(w, r, feedNames[0], cfg, traffic, accept)
  568. }
  569. } else {
  570. resource := path[1]
  571. switch resource {
  572. case "queryables":
  573. err = handleQueryables(w, r, feedNames, cfg, traffic, accept)
  574. case "locatables":
  575. err = handleLocatables(w, r, feedNames, cfg, traffic, accept)
  576. case "departures":
  577. if len(feedNames) > 1 {
  578. err = ServerError{
  579. code: http.StatusBadRequest,
  580. field: "feed",
  581. value: path[0],
  582. }
  583. break
  584. }
  585. err = handleDepartures(w, r, feedNames[0], cfg, traffic, accept)
  586. case "lines":
  587. if len(feedNames) > 1 {
  588. err = ServerError{
  589. code: http.StatusBadRequest,
  590. field: "feed",
  591. value: path[0],
  592. }
  593. break
  594. }
  595. err = handleLine(w, r, feedNames[0], cfg, traffic, accept)
  596. /*case "trips":
  597. if len(feedNames) > 1 {
  598. err = ServerError{
  599. code: http.StatusBadRequest,
  600. field: "feed",
  601. value: path[0],
  602. }
  603. break
  604. }
  605. err = handleTrip(w, r, feedNames[0], cfg, traffic)*/
  606. // todo(BAF21, BAF11): "shape" (line_id/trip_id)
  607. default:
  608. err = ServerError{
  609. code: http.StatusNotFound,
  610. field: "resource",
  611. value: resource,
  612. }
  613. }
  614. }
  615. }
  616. if err != nil {
  617. sendError(w, r, err)
  618. }
  619. })
  620. go func() {
  621. if err := srv.ListenAndServe(); err != http.ErrServerClosed {
  622. log.Printf("ListenAndServe(): %v\n", err)
  623. os.Exit(1)
  624. }
  625. }()
  626. return srv
  627. }