router.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832
  1. package server
  2. import (
  3. "apiote.xyz/p/szczanieckiej/api"
  4. "apiote.xyz/p/szczanieckiej/config"
  5. "apiote.xyz/p/szczanieckiej/traffic"
  6. traffic_errors "apiote.xyz/p/szczanieckiej/traffic/errors"
  7. "errors"
  8. "fmt"
  9. "log"
  10. "net/http"
  11. "os"
  12. "strconv"
  13. "strings"
  14. "time"
  15. "golang.org/x/text/language"
  16. "git.sr.ht/~sircmpwn/go-bare"
  17. )
  18. type ServerError struct {
  19. code int
  20. field string
  21. value string
  22. err error
  23. }
  24. func (e ServerError) Error() string {
  25. message := ""
  26. switch e.code {
  27. case http.StatusBadRequest:
  28. message = e.value + " not valid as " + e.field
  29. case http.StatusNotFound:
  30. message = e.value + " not found as " + e.field
  31. default:
  32. message = fmt.Sprintf("error %d", e.code)
  33. if e.field != "" {
  34. message += " in field " + e.field
  35. }
  36. if e.value != "" {
  37. message += " with value " + e.value
  38. }
  39. if e.err != nil {
  40. message += ": " + e.err.Error()
  41. }
  42. }
  43. return message
  44. }
  45. func parseDate(dateString string, feedName string, t *traffic.Traffic) (traffic.Validity, time.Time, error) {
  46. versionCode := traffic.Validity("")
  47. if dateString == "" {
  48. feedNow := time.Now().In(t.Feeds[feedName].GetLocation())
  49. dateString = feedNow.Format(traffic.DateFormat)
  50. }
  51. date, err := time.ParseInLocation(traffic.DateFormat, dateString, t.Feeds[feedName].GetLocation())
  52. if err != nil {
  53. return versionCode, date, ServerError{
  54. code: http.StatusBadRequest,
  55. field: "date",
  56. value: dateString,
  57. }
  58. }
  59. for _, v := range t.Versions[feedName] {
  60. if !v.ValidFrom.After(date) && !date.After(v.ValidTill) {
  61. versionCode = traffic.Validity(v.String())
  62. }
  63. }
  64. if versionCode == "" {
  65. return versionCode, date, ServerError{
  66. code: http.StatusNotFound,
  67. field: "date",
  68. value: dateString,
  69. }
  70. }
  71. return versionCode, date, nil
  72. }
  73. func parsePosition(location string) (traffic.Position, error) {
  74. locationString := strings.Split(location, ",")
  75. if len(locationString) != 2 {
  76. return traffic.Position{}, fmt.Errorf("location is not two numbers")
  77. }
  78. lat, err := strconv.ParseFloat(locationString[0], 64)
  79. if err != nil {
  80. return traffic.Position{}, fmt.Errorf("latitude is not a float")
  81. }
  82. lon, err := strconv.ParseFloat(locationString[1], 64)
  83. if err != nil {
  84. return traffic.Position{}, fmt.Errorf("longitude is not a float")
  85. }
  86. return traffic.Position{Lat: lat, Lon: lon}, nil
  87. }
  88. func handleRoot(w http.ResponseWriter, r *http.Request, t *traffic.Traffic, cfg config.Config, accept uint) error {
  89. if accept > 2 {
  90. return ServerError{
  91. code: http.StatusNotAcceptable,
  92. }
  93. }
  94. var success api.FeedsResponse
  95. switch accept {
  96. case 0:
  97. success = api.FeedsResponseDev{}
  98. case 1:
  99. success = api.FeedsResponseV1{}
  100. default:
  101. return ServerError{
  102. code: http.StatusNotAcceptable,
  103. }
  104. }
  105. acceptLanguage := r.Header.Get("Accept-Language")
  106. if acceptLanguage == "" {
  107. acceptLanguage = "und"
  108. }
  109. preferredLanguages, _, err := language.ParseAcceptLanguage(acceptLanguage)
  110. if err != nil {
  111. return ServerError{
  112. code: http.StatusBadRequest,
  113. field: "Accept-Language",
  114. value: acceptLanguage,
  115. err: err,
  116. }
  117. }
  118. for id, feed := range t.Feeds {
  119. lastUpdate, err := traffic.LastUpdate(cfg.FeedsPath, feed.String())
  120. if err != nil {
  121. return fmt.Errorf("while getting last update for %s: %w", id, err)
  122. }
  123. trafficDescription := feed.Description()
  124. descriptionTags := []language.Tag{}
  125. for t := range trafficDescription {
  126. descriptionTags = append(descriptionTags, t)
  127. }
  128. trafficAttribution := feed.Attribution()
  129. attributionTags := []language.Tag{}
  130. for t := range trafficAttribution {
  131. attributionTags = append(attributionTags, t)
  132. }
  133. matcher := language.NewMatcher(preferredLanguages)
  134. _, index, _ := matcher.Match(descriptionTags...)
  135. descriptionTag := preferredLanguages[index]
  136. _, index, _ = matcher.Match(attributionTags...)
  137. attributionTag := preferredLanguages[index]
  138. f := api.FeedInfoV1{
  139. Name: feed.Name(),
  140. Id: id,
  141. Attribution: trafficAttribution[attributionTag],
  142. Description: trafficDescription[descriptionTag],
  143. LastUpdate: lastUpdate.Format(time.RFC3339),
  144. }
  145. switch accept {
  146. case 0:
  147. s := success.(api.FeedsResponseDev)
  148. s.Feeds = append(s.Feeds, f)
  149. success = s
  150. case 1:
  151. s := success.(api.FeedsResponseV1)
  152. s.Feeds = append(s.Feeds, f)
  153. success = s
  154. }
  155. }
  156. var response api.FeedsResponse = success
  157. bytes, err := bare.Marshal(&response)
  158. if err != nil {
  159. return fmt.Errorf("while marshaling: %w", err)
  160. }
  161. _, err = w.Write(bytes)
  162. if err != nil {
  163. return fmt.Errorf("while writing: %w", err)
  164. }
  165. return nil
  166. }
  167. func handleFeed(w http.ResponseWriter, r *http.Request, feedName string, accept uint) error {
  168. return ServerError{
  169. code: http.StatusNotFound,
  170. field: "resource",
  171. value: "feed",
  172. }
  173. // todo(BAF17) send feed
  174. }
  175. func handleLocatables(w http.ResponseWriter, r *http.Request, feedNames []string, cfg config.Config, t *traffic.Traffic, accept uint) error {
  176. if accept > 2 {
  177. return ServerError{
  178. code: http.StatusNotAcceptable,
  179. }
  180. }
  181. var locatablesSuccess api.LocatablesResponse
  182. switch accept {
  183. case 0:
  184. locatablesSuccess = api.LocatablesResponseDev{}
  185. case 1:
  186. locatablesSuccess = api.LocatablesResponseV1{}
  187. case 2:
  188. locatablesSuccess = api.LocatablesResponseV2{}
  189. }
  190. err := r.ParseForm()
  191. if err != nil {
  192. return fmt.Errorf("while parsing form: %w", err)
  193. }
  194. dateString := r.Form.Get("date")
  195. lb, err := parsePosition(r.Form.Get("lb"))
  196. if err != nil {
  197. return ServerError{
  198. code: http.StatusBadRequest,
  199. field: "lb",
  200. value: r.Form.Get("lb"),
  201. err: err,
  202. }
  203. }
  204. rt, err := parsePosition(r.Form.Get("rt"))
  205. if err != nil {
  206. return ServerError{
  207. code: http.StatusBadRequest,
  208. field: "rt",
  209. value: r.Form.Get("rt"),
  210. err: err,
  211. }
  212. }
  213. for _, feedName := range feedNames {
  214. versionCode, _, err := parseDate(dateString, feedName, t)
  215. if err != nil {
  216. return fmt.Errorf("while parsing date: %w", err)
  217. }
  218. context := traffic.Context{
  219. DataHome: cfg.FeedsPath,
  220. FeedName: feedName,
  221. Version: versionCode,
  222. }
  223. stops, err := traffic.GetStopsIn(lb, rt, context, t)
  224. if err != nil {
  225. return fmt.Errorf("while getting stops in bounding box: %w", err)
  226. }
  227. vehicles, err := traffic.GetVehiclesIn(lb, rt, context, t)
  228. if err != nil {
  229. return fmt.Errorf("while getting vehicles in bounding box: %w", err)
  230. }
  231. locatables := []traffic.Locatable{}
  232. for _, stop := range stops {
  233. locatables = append(locatables, stop)
  234. }
  235. for _, vehicle := range vehicles {
  236. locatables = append(locatables, vehicle)
  237. }
  238. switch accept {
  239. case 0:
  240. locatablesSuccess, err = api.CreateSuccessLocatablesV2(locatables, context, t, locatablesSuccess)
  241. case 1:
  242. locatablesSuccess, err = api.CreateSuccessLocatables(locatables, context, t, locatablesSuccess)
  243. case 2:
  244. locatablesSuccess, err = api.CreateSuccessLocatablesV2(locatables, context, t, locatablesSuccess)
  245. }
  246. if err != nil {
  247. return fmt.Errorf("while creating locatablesSuccess from near locatables: %w", err)
  248. }
  249. }
  250. bytes, err := bare.Marshal(&locatablesSuccess)
  251. if err != nil {
  252. return fmt.Errorf("while marshaling: %w", err)
  253. }
  254. _, err = w.Write(bytes)
  255. if err != nil {
  256. return fmt.Errorf("while writing: %w", err)
  257. }
  258. return nil
  259. }
  260. func handleQueryables(w http.ResponseWriter, r *http.Request, feedNames []string, cfg config.Config, t *traffic.Traffic, accept uint) error {
  261. if accept > 2 {
  262. return ServerError{
  263. code: http.StatusNotAcceptable,
  264. }
  265. }
  266. var queryablesSuccess api.QueryablesResponse
  267. switch accept {
  268. case 0:
  269. queryablesSuccess = api.QueryablesResponseDev{}
  270. case 1:
  271. queryablesSuccess = api.QueryablesResponseV1{}
  272. case 2:
  273. queryablesSuccess = api.QueryablesResponseV2{}
  274. }
  275. err := r.ParseForm()
  276. if err != nil {
  277. return fmt.Errorf("while parsing form: %w", err)
  278. }
  279. query := r.Form.Get("q")
  280. near := r.Form.Get("near")
  281. dateString := r.Form.Get("date")
  282. limitString := r.Form.Get("limit")
  283. if limitString == "" {
  284. limitString = "12"
  285. }
  286. limit, err := strconv.ParseUint(limitString, 10, 0)
  287. if err != nil {
  288. return ServerError{
  289. code: http.StatusBadRequest,
  290. field: "limit",
  291. value: limitString,
  292. }
  293. }
  294. offsetString := r.Form.Get("offset")
  295. if offsetString == "" {
  296. offsetString = "0"
  297. }
  298. offset, err := strconv.ParseUint(offsetString, 10, 0)
  299. if err != nil {
  300. return ServerError{
  301. code: http.StatusBadRequest,
  302. field: "offset",
  303. value: offsetString,
  304. }
  305. }
  306. for _, feedName := range feedNames {
  307. versionCode, _, err := parseDate(dateString, feedName, t)
  308. if err != nil {
  309. return fmt.Errorf("while parsing date: %w", err)
  310. }
  311. context := traffic.Context{
  312. DataHome: cfg.FeedsPath,
  313. FeedName: feedName,
  314. Version: versionCode,
  315. }
  316. if near != "" {
  317. location, err := parsePosition(near)
  318. if err != nil {
  319. return ServerError{
  320. code: http.StatusBadRequest,
  321. field: "near",
  322. value: near,
  323. err: err,
  324. }
  325. }
  326. stops, err := traffic.GetStopsNear(location, context, t)
  327. if err != nil {
  328. return fmt.Errorf("while getting near stops: %w", err)
  329. }
  330. items := []traffic.Item{}
  331. for _, stop := range stops {
  332. items = append(items, stop)
  333. }
  334. switch accept {
  335. case 0:
  336. queryablesSuccess, err = api.CreateSuccessQueryablesV2(query, items, context, t, queryablesSuccess)
  337. case 1:
  338. queryablesSuccess, err = api.CreateSuccessQueryables(items, context, t, queryablesSuccess)
  339. case 2:
  340. queryablesSuccess, err = api.CreateSuccessQueryablesV2(query, items, context, t, queryablesSuccess)
  341. }
  342. if err != nil {
  343. return fmt.Errorf("while creating stopsSuccess from near stops: %w", err)
  344. }
  345. } else {
  346. ix := t.CodeIndexes[feedName][versionCode]
  347. code := query
  348. _, exists := ix[code]
  349. if exists {
  350. stop, err := traffic.GetStop(code, context, t)
  351. if err != nil {
  352. return fmt.Errorf("while getting stop: %w", err)
  353. }
  354. switch accept {
  355. case 0:
  356. queryablesSuccess, err = api.CreateSuccessQueryablesV2(query, []traffic.Item{stop}, context, t, queryablesSuccess)
  357. case 1:
  358. queryablesSuccess, err = api.CreateSuccessQueryables([]traffic.Item{stop}, context, t, queryablesSuccess)
  359. case 2:
  360. queryablesSuccess, err = api.CreateSuccessQueryablesV2(query, []traffic.Item{stop}, context, t, queryablesSuccess)
  361. }
  362. if err != nil {
  363. return fmt.Errorf("while creating stopsSuccess from code: %w", err)
  364. }
  365. } else {
  366. query, err = traffic.CleanQuery(query, t.Feeds[feedName])
  367. if err != nil {
  368. return fmt.Errorf("while cleaning query: %w", err)
  369. }
  370. lines, err1 := traffic.QueryLines(query, cfg.FeedsPath, feedName, versionCode, t)
  371. stops, err2 := traffic.QueryStops(query, context, t)
  372. if err1 != nil && err2 != nil {
  373. return fmt.Errorf("while querying stops and lines: %w", errors.Join(err1, err2))
  374. }
  375. items := []traffic.Item{}
  376. for _, line := range lines {
  377. items = append(items, line)
  378. }
  379. for _, stop := range stops {
  380. items = append(items, stop)
  381. }
  382. switch accept {
  383. case 0:
  384. queryablesSuccess, err = api.CreateSuccessQueryablesV2(query, items, context, t, queryablesSuccess)
  385. case 1:
  386. queryablesSuccess, err = api.CreateSuccessQueryables(items, context, t, queryablesSuccess)
  387. case 2:
  388. queryablesSuccess, err = api.CreateSuccessQueryablesV2(query, items, context, t, queryablesSuccess)
  389. }
  390. if err != nil {
  391. return fmt.Errorf("while creating stopsSuccess from lines and stops: %w", err)
  392. }
  393. }
  394. }
  395. }
  396. queryablesSuccess = api.LimitQueryables(queryablesSuccess, offset, limit)
  397. bytes, err := bare.Marshal(&queryablesSuccess)
  398. if err != nil {
  399. return fmt.Errorf("while marshaling: %w", err)
  400. }
  401. _, err = w.Write(bytes)
  402. if err != nil {
  403. return fmt.Errorf("while writing: %w", err)
  404. }
  405. return nil
  406. }
  407. func handleDepartures(w http.ResponseWriter, r *http.Request, feedName string, cfg config.Config, t *traffic.Traffic, accept uint) error {
  408. if accept > 2 {
  409. return ServerError{
  410. code: http.StatusNotAcceptable,
  411. }
  412. }
  413. err := r.ParseForm()
  414. if err != nil {
  415. return fmt.Errorf("while parsing form: %w", err)
  416. }
  417. code := r.Form.Get("code")
  418. if code == "" {
  419. return ServerError{
  420. code: http.StatusBadRequest,
  421. field: "code",
  422. value: "EMPTY",
  423. }
  424. }
  425. dateString := r.Form.Get("date")
  426. line := r.Form.Get("line")
  427. limitString := r.Form.Get("limit")
  428. if limitString == "" {
  429. limitString = "12"
  430. }
  431. limit, err := strconv.ParseUint(limitString, 10, 0)
  432. if err != nil {
  433. return ServerError{
  434. code: http.StatusBadRequest,
  435. field: "limit",
  436. value: limitString,
  437. }
  438. }
  439. offsetString := r.Form.Get("offset")
  440. if offsetString == "" {
  441. offsetString = "0"
  442. }
  443. offset, err := strconv.ParseUint(offsetString, 10, 0)
  444. if err != nil {
  445. return ServerError{
  446. code: http.StatusBadRequest,
  447. field: "offset",
  448. value: offsetString,
  449. }
  450. }
  451. departuresType := traffic.DEPARTURES_FULL
  452. if dateString == "" {
  453. departuresType = traffic.DEPARTURES_HYBRID
  454. }
  455. versionCode, date, err := parseDate(dateString, feedName, t)
  456. if err != nil {
  457. return err
  458. }
  459. context := traffic.Context{
  460. DataHome: cfg.FeedsPath,
  461. FeedName: feedName,
  462. Version: versionCode,
  463. }
  464. ix := t.CodeIndexes[feedName][versionCode]
  465. _, exists := ix[code]
  466. if !exists {
  467. return ServerError{
  468. code: http.StatusNotFound,
  469. field: "code",
  470. value: string(code),
  471. }
  472. }
  473. stop, err := traffic.GetStop(code, context, t)
  474. if err != nil {
  475. return fmt.Errorf("while getting stop: %w", err)
  476. }
  477. departures, err := traffic.GetDepartures(code, line, cfg.FeedsPath, feedName, versionCode, t, date, departuresType)
  478. if err != nil {
  479. if _, ok := err.(traffic_errors.NoSchedule); ok {
  480. return ServerError{
  481. code: http.StatusNotFound,
  482. field: "date",
  483. value: dateString,
  484. }
  485. } else {
  486. return fmt.Errorf("while getting departures: %w", err)
  487. }
  488. }
  489. if int(offset) > len(departures) {
  490. departures = []traffic.DepartureRealtime{}
  491. } else if len(departures) < int(offset+limit) {
  492. departures = departures[offset:]
  493. } else {
  494. departures = departures[offset : offset+limit]
  495. }
  496. acceptLanguage := r.Header.Get("Accept-Language")
  497. if acceptLanguage == "" {
  498. acceptLanguage, err = traffic.GetLanguage(context, t)
  499. if err != nil {
  500. log.Printf("while gettng default language: %v\n", err)
  501. acceptLanguage = "und"
  502. }
  503. }
  504. preferredLanguages, _, err := language.ParseAcceptLanguage(acceptLanguage)
  505. if err != nil {
  506. return ServerError{
  507. code: http.StatusBadRequest,
  508. field: "Accept-Language",
  509. value: acceptLanguage,
  510. err: err,
  511. }
  512. }
  513. alerts, err := traffic.GetAlerts(string(code), preferredLanguages, context, t)
  514. if err != nil {
  515. return fmt.Errorf("while getting alerts: %w", err)
  516. }
  517. var success api.DeparturesResponse
  518. switch accept {
  519. case 0:
  520. success, err = api.CreateSuccessDeparturesV2(stop, departures, date, t.Vehicles[feedName][versionCode], alerts, context, t, accept)
  521. case 1:
  522. success, err = api.CreateSuccessDeparturesV1(stop, departures, date, t.Vehicles[feedName][versionCode], alerts, context, t, accept)
  523. case 2:
  524. success, err = api.CreateSuccessDeparturesV2(stop, departures, date, t.Vehicles[feedName][versionCode], alerts, context, t, accept)
  525. }
  526. if err != nil {
  527. return fmt.Errorf("while creating departuresSuccess: %w", err)
  528. }
  529. bytes, err := bare.Marshal(&success)
  530. if err != nil {
  531. return fmt.Errorf("while marshaling: %w", err)
  532. }
  533. _, err = w.Write(bytes)
  534. if err != nil {
  535. return fmt.Errorf("while writing: %w", err)
  536. }
  537. return nil
  538. }
  539. func handleTrip(w http.ResponseWriter, r *http.Request, feedName string, cfg config.Config, t *traffic.Traffic, accept uint) error {
  540. path := strings.Split(r.URL.Path[1:], "/")
  541. if len(path) == 3 {
  542. dateString := r.Form.Get("date")
  543. versionCode, _, err := parseDate(dateString, feedName, t)
  544. if err != nil {
  545. return err
  546. }
  547. tripID := path[2]
  548. stopCode := r.Form.Get("stop")
  549. context := traffic.Context{
  550. DataHome: cfg.FeedsPath,
  551. FeedName: feedName,
  552. Version: versionCode,
  553. }
  554. trip, err := traffic.GetTripFromStop(tripID, stopCode, context, t)
  555. if err != nil {
  556. return fmt.Errorf("while getting line: %w", err)
  557. }
  558. if len(trip) == 0 {
  559. return ServerError{
  560. code: http.StatusNotFound,
  561. field: "line",
  562. value: tripID,
  563. }
  564. }
  565. success := struct{}{} //api.CreateSuccessTrip(trip)
  566. bytes, err := bare.Marshal(&success)
  567. if err != nil {
  568. return fmt.Errorf("while marshaling trip: %w", err)
  569. }
  570. _, err = w.Write(bytes)
  571. if err != nil {
  572. return fmt.Errorf("while writing: %w", err)
  573. }
  574. } else {
  575. return ServerError{
  576. code: http.StatusNotFound,
  577. field: "trip",
  578. value: "EMPTY",
  579. }
  580. }
  581. return nil
  582. }
  583. func handleLine(w http.ResponseWriter, r *http.Request, feedName string, cfg config.Config, t *traffic.Traffic, accept uint) error {
  584. if accept > 1 {
  585. return ServerError{
  586. code: http.StatusNotAcceptable,
  587. }
  588. }
  589. path := strings.Split(r.URL.Path[1:], "/")
  590. if len(path) == 3 {
  591. dateString := r.Form.Get("date")
  592. versionCode, _, err := parseDate(dateString, feedName, t)
  593. if err != nil {
  594. return err
  595. }
  596. name := path[2]
  597. context := traffic.Context{
  598. DataHome: cfg.FeedsPath,
  599. FeedName: feedName,
  600. Version: versionCode,
  601. }
  602. line, err := traffic.GetLine(name, context, t)
  603. if err != nil {
  604. return fmt.Errorf("while getting line: %w", err)
  605. }
  606. if line.Name == "" {
  607. return ServerError{
  608. code: http.StatusNotFound,
  609. field: "line",
  610. value: name,
  611. }
  612. }
  613. var success api.LineResponse
  614. switch accept {
  615. case 0:
  616. success, err = api.CreateSuccessLine(line, context, t)
  617. case 1:
  618. success, err = api.CreateSuccessLine(line, context, t)
  619. }
  620. if err != nil {
  621. return fmt.Errorf("while creating response: %w", err)
  622. }
  623. bytes, err := bare.Marshal(&success)
  624. if err != nil {
  625. return fmt.Errorf("while marshaling line: %w", err)
  626. }
  627. _, err = w.Write(bytes)
  628. if err != nil {
  629. return fmt.Errorf("while writing: %w", err)
  630. }
  631. } else {
  632. return ServerError{
  633. code: http.StatusNotFound,
  634. field: "line",
  635. value: "EMPTY",
  636. }
  637. }
  638. return nil
  639. }
  640. func sendError(w http.ResponseWriter, r *http.Request, err error) {
  641. var (
  642. se ServerError
  643. response api.ErrorResponse
  644. )
  645. if !errors.As(err, &se) {
  646. se = ServerError{
  647. code: http.StatusInternalServerError,
  648. err: err,
  649. }
  650. }
  651. response = api.ErrorResponse{
  652. Field: se.field,
  653. Message: se.Error(),
  654. }
  655. log.Println(err.Error())
  656. b, err := bare.Marshal(&response)
  657. if err != nil {
  658. w.WriteHeader(http.StatusInternalServerError)
  659. return
  660. }
  661. w.WriteHeader(se.code)
  662. w.Write(b)
  663. }
  664. func parseAccept(headers []string) (uint, error) {
  665. accept := -1
  666. for _, header := range headers {
  667. if header == "" {
  668. header = "EMPTY"
  669. }
  670. header, _ := strings.CutPrefix(header, "application/")
  671. header, _ = strings.CutSuffix(header, "+bare")
  672. a, err := strconv.ParseUint(header, 10, 0)
  673. if err != nil {
  674. continue
  675. }
  676. if a == 0 {
  677. return 0, nil
  678. }
  679. if int(a) > accept {
  680. accept = int(a)
  681. }
  682. }
  683. if accept == -1 {
  684. return 0, ServerError{
  685. code: http.StatusBadRequest,
  686. field: "Accept",
  687. value: "",
  688. }
  689. }
  690. return uint(accept), nil
  691. }
  692. func Route(cfg config.Config, traffic *traffic.Traffic) *http.Server {
  693. srv := &http.Server{Addr: cfg.ListenAddress}
  694. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  695. accept, err := parseAccept(r.Header.Values("Accept"))
  696. if err != nil {
  697. sendError(w, r, fmt.Errorf("while parsing accept: %w", err))
  698. return
  699. }
  700. if r.URL.Path[1:] == "" {
  701. err = handleRoot(w, r, traffic, cfg, accept)
  702. } else {
  703. path := strings.Split(r.URL.Path[1:], "/")
  704. feedNames := strings.Split(path[0], ",")
  705. for _, feedName := range feedNames {
  706. if traffic.Versions[feedName] == nil {
  707. sendError(w, r, ServerError{
  708. code: http.StatusNotFound,
  709. field: "feed",
  710. value: feedName,
  711. })
  712. return
  713. }
  714. }
  715. if len(path) == 1 {
  716. if len(feedNames) > 1 {
  717. err = ServerError{
  718. code: http.StatusBadRequest,
  719. field: "feed",
  720. value: path[0],
  721. }
  722. } else {
  723. err = handleFeed(w, r, feedNames[0], accept)
  724. }
  725. } else {
  726. resource := path[1]
  727. switch resource {
  728. case "queryables":
  729. err = handleQueryables(w, r, feedNames, cfg, traffic, accept)
  730. case "locatables":
  731. err = handleLocatables(w, r, feedNames, cfg, traffic, accept)
  732. case "departures":
  733. if len(feedNames) > 1 {
  734. err = ServerError{
  735. code: http.StatusBadRequest,
  736. field: "feed",
  737. value: path[0],
  738. }
  739. break
  740. }
  741. err = handleDepartures(w, r, feedNames[0], cfg, traffic, accept)
  742. case "lines":
  743. if len(feedNames) > 1 {
  744. err = ServerError{
  745. code: http.StatusBadRequest,
  746. field: "feed",
  747. value: path[0],
  748. }
  749. break
  750. }
  751. err = handleLine(w, r, feedNames[0], cfg, traffic, accept)
  752. /*case "trips":
  753. if len(feedNames) > 1 {
  754. err = ServerError{
  755. code: http.StatusBadRequest,
  756. field: "feed",
  757. value: path[0],
  758. }
  759. break
  760. }
  761. err = handleTrip(w, r, feedNames[0], cfg, traffic)*/
  762. // todo(BAF21, BAF11): "shape" (line_id/trip_id)
  763. default:
  764. err = ServerError{
  765. code: http.StatusNotFound,
  766. field: "resource",
  767. value: resource,
  768. }
  769. }
  770. }
  771. }
  772. if err != nil {
  773. sendError(w, r, err)
  774. }
  775. })
  776. go func() {
  777. fmt.Println("The wheels on the bus go round and round")
  778. if err := srv.ListenAndServe(); err != http.ErrServerClosed {
  779. log.Printf("ListenAndServe(): %v", err)
  780. os.Exit(1)
  781. }
  782. }()
  783. return srv
  784. }