url_test.go 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064
  1. // Copyright 2009 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package url
  5. import (
  6. "bytes"
  7. encodingPkg "encoding"
  8. "encoding/gob"
  9. "encoding/json"
  10. "fmt"
  11. "io"
  12. "net"
  13. "reflect"
  14. "strings"
  15. "testing"
  16. )
  17. type URLTest struct {
  18. in string
  19. out *URL // expected parse
  20. roundtrip string // expected result of reserializing the URL; empty means same as "in".
  21. }
  22. var urltests = []URLTest{
  23. // no path
  24. {
  25. "http://www.google.com",
  26. &URL{
  27. Scheme: "http",
  28. Host: "www.google.com",
  29. },
  30. "",
  31. },
  32. // path
  33. {
  34. "http://www.google.com/",
  35. &URL{
  36. Scheme: "http",
  37. Host: "www.google.com",
  38. Path: "/",
  39. },
  40. "",
  41. },
  42. // path with hex escaping
  43. {
  44. "http://www.google.com/file%20one%26two",
  45. &URL{
  46. Scheme: "http",
  47. Host: "www.google.com",
  48. Path: "/file one&two",
  49. RawPath: "/file%20one%26two",
  50. },
  51. "",
  52. },
  53. // fragment with hex escaping
  54. {
  55. "http://www.google.com/#file%20one%26two",
  56. &URL{
  57. Scheme: "http",
  58. Host: "www.google.com",
  59. Path: "/",
  60. Fragment: "file one&two",
  61. RawFragment: "file%20one%26two",
  62. },
  63. "",
  64. },
  65. // user
  66. {
  67. "ftp://webmaster@www.google.com/",
  68. &URL{
  69. Scheme: "ftp",
  70. User: User("webmaster"),
  71. Host: "www.google.com",
  72. Path: "/",
  73. },
  74. "",
  75. },
  76. // escape sequence in username
  77. {
  78. "ftp://john%20doe@www.google.com/",
  79. &URL{
  80. Scheme: "ftp",
  81. User: User("john doe"),
  82. Host: "www.google.com",
  83. Path: "/",
  84. },
  85. "ftp://john%20doe@www.google.com/",
  86. },
  87. // empty query
  88. {
  89. "http://www.google.com/?",
  90. &URL{
  91. Scheme: "http",
  92. Host: "www.google.com",
  93. Path: "/",
  94. ForceQuery: true,
  95. },
  96. "",
  97. },
  98. // query ending in question mark (Issue 14573)
  99. {
  100. "http://www.google.com/?foo=bar?",
  101. &URL{
  102. Scheme: "http",
  103. Host: "www.google.com",
  104. Path: "/",
  105. RawQuery: "foo=bar?",
  106. },
  107. "",
  108. },
  109. // query
  110. {
  111. "http://www.google.com/?q=go+language",
  112. &URL{
  113. Scheme: "http",
  114. Host: "www.google.com",
  115. Path: "/",
  116. RawQuery: "q=go+language",
  117. },
  118. "",
  119. },
  120. // query with hex escaping: NOT parsed
  121. {
  122. "http://www.google.com/?q=go%20language",
  123. &URL{
  124. Scheme: "http",
  125. Host: "www.google.com",
  126. Path: "/",
  127. RawQuery: "q=go%20language",
  128. },
  129. "",
  130. },
  131. // %20 outside query
  132. {
  133. "http://www.google.com/a%20b?q=c+d",
  134. &URL{
  135. Scheme: "http",
  136. Host: "www.google.com",
  137. Path: "/a b",
  138. RawQuery: "q=c+d",
  139. },
  140. "",
  141. },
  142. // path without leading /, so no parsing
  143. {
  144. "http:www.google.com/?q=go+language",
  145. &URL{
  146. Scheme: "http",
  147. Opaque: "www.google.com/",
  148. RawQuery: "q=go+language",
  149. },
  150. "http:www.google.com/?q=go+language",
  151. },
  152. // path without leading /, so no parsing
  153. {
  154. "http:%2f%2fwww.google.com/?q=go+language",
  155. &URL{
  156. Scheme: "http",
  157. Opaque: "%2f%2fwww.google.com/",
  158. RawQuery: "q=go+language",
  159. },
  160. "http:%2f%2fwww.google.com/?q=go+language",
  161. },
  162. // non-authority with path
  163. {
  164. "mailto:/webmaster@golang.org",
  165. &URL{
  166. Scheme: "mailto",
  167. Path: "/webmaster@golang.org",
  168. },
  169. "mailto:///webmaster@golang.org", // unfortunate compromise
  170. },
  171. // non-authority
  172. {
  173. "mailto:webmaster@golang.org",
  174. &URL{
  175. Scheme: "mailto",
  176. Opaque: "webmaster@golang.org",
  177. },
  178. "",
  179. },
  180. // unescaped :// in query should not create a scheme
  181. {
  182. "/foo?query=http://bad",
  183. &URL{
  184. Path: "/foo",
  185. RawQuery: "query=http://bad",
  186. },
  187. "",
  188. },
  189. // leading // without scheme should create an authority
  190. {
  191. "//foo",
  192. &URL{
  193. Host: "foo",
  194. },
  195. "",
  196. },
  197. // leading // without scheme, with userinfo, path, and query
  198. {
  199. "//user@foo/path?a=b",
  200. &URL{
  201. User: User("user"),
  202. Host: "foo",
  203. Path: "/path",
  204. RawQuery: "a=b",
  205. },
  206. "",
  207. },
  208. // Three leading slashes isn't an authority, but doesn't return an error.
  209. // (We can't return an error, as this code is also used via
  210. // ServeHTTP -> ReadRequest -> Parse, which is arguably a
  211. // different URL parsing context, but currently shares the
  212. // same codepath)
  213. {
  214. "///threeslashes",
  215. &URL{
  216. Path: "///threeslashes",
  217. },
  218. "",
  219. },
  220. {
  221. "http://user:password@google.com",
  222. &URL{
  223. Scheme: "http",
  224. User: UserPassword("user", "password"),
  225. Host: "google.com",
  226. },
  227. "http://user:password@google.com",
  228. },
  229. // unescaped @ in username should not confuse host
  230. {
  231. "http://j@ne:password@google.com",
  232. &URL{
  233. Scheme: "http",
  234. User: UserPassword("j@ne", "password"),
  235. Host: "google.com",
  236. },
  237. "http://j%40ne:password@google.com",
  238. },
  239. // unescaped @ in password should not confuse host
  240. {
  241. "http://jane:p@ssword@google.com",
  242. &URL{
  243. Scheme: "http",
  244. User: UserPassword("jane", "p@ssword"),
  245. Host: "google.com",
  246. },
  247. "http://jane:p%40ssword@google.com",
  248. },
  249. {
  250. "http://j@ne:password@google.com/p@th?q=@go",
  251. &URL{
  252. Scheme: "http",
  253. User: UserPassword("j@ne", "password"),
  254. Host: "google.com",
  255. Path: "/p@th",
  256. RawQuery: "q=@go",
  257. },
  258. "http://j%40ne:password@google.com/p@th?q=@go",
  259. },
  260. {
  261. "http://www.google.com/?q=go+language#foo",
  262. &URL{
  263. Scheme: "http",
  264. Host: "www.google.com",
  265. Path: "/",
  266. RawQuery: "q=go+language",
  267. Fragment: "foo",
  268. },
  269. "",
  270. },
  271. {
  272. "http://www.google.com/?q=go+language#foo&bar",
  273. &URL{
  274. Scheme: "http",
  275. Host: "www.google.com",
  276. Path: "/",
  277. RawQuery: "q=go+language",
  278. Fragment: "foo&bar",
  279. },
  280. "http://www.google.com/?q=go+language#foo&bar",
  281. },
  282. {
  283. "http://www.google.com/?q=go+language#foo%26bar",
  284. &URL{
  285. Scheme: "http",
  286. Host: "www.google.com",
  287. Path: "/",
  288. RawQuery: "q=go+language",
  289. Fragment: "foo&bar",
  290. RawFragment: "foo%26bar",
  291. },
  292. "http://www.google.com/?q=go+language#foo%26bar",
  293. },
  294. {
  295. "file:///home/adg/rabbits",
  296. &URL{
  297. Scheme: "file",
  298. Host: "",
  299. Path: "/home/adg/rabbits",
  300. },
  301. "file:///home/adg/rabbits",
  302. },
  303. // "Windows" paths are no exception to the rule.
  304. // See golang.org/issue/6027, especially comment #9.
  305. {
  306. "file:///C:/FooBar/Baz.txt",
  307. &URL{
  308. Scheme: "file",
  309. Host: "",
  310. Path: "/C:/FooBar/Baz.txt",
  311. },
  312. "file:///C:/FooBar/Baz.txt",
  313. },
  314. // case-insensitive scheme
  315. {
  316. "MaIlTo:webmaster@golang.org",
  317. &URL{
  318. Scheme: "mailto",
  319. Opaque: "webmaster@golang.org",
  320. },
  321. "mailto:webmaster@golang.org",
  322. },
  323. // Relative path
  324. {
  325. "a/b/c",
  326. &URL{
  327. Path: "a/b/c",
  328. },
  329. "a/b/c",
  330. },
  331. // escaped '?' in username and password
  332. {
  333. "http://%3Fam:pa%3Fsword@google.com",
  334. &URL{
  335. Scheme: "http",
  336. User: UserPassword("?am", "pa?sword"),
  337. Host: "google.com",
  338. },
  339. "",
  340. },
  341. // host subcomponent; IPv4 address in RFC 3986
  342. {
  343. "http://192.168.0.1/",
  344. &URL{
  345. Scheme: "http",
  346. Host: "192.168.0.1",
  347. Path: "/",
  348. },
  349. "",
  350. },
  351. // host and port subcomponents; IPv4 address in RFC 3986
  352. {
  353. "http://192.168.0.1:8080/",
  354. &URL{
  355. Scheme: "http",
  356. Host: "192.168.0.1:8080",
  357. Path: "/",
  358. },
  359. "",
  360. },
  361. // host subcomponent; IPv6 address in RFC 3986
  362. {
  363. "http://[fe80::1]/",
  364. &URL{
  365. Scheme: "http",
  366. Host: "[fe80::1]",
  367. Path: "/",
  368. },
  369. "",
  370. },
  371. // host and port subcomponents; IPv6 address in RFC 3986
  372. {
  373. "http://[fe80::1]:8080/",
  374. &URL{
  375. Scheme: "http",
  376. Host: "[fe80::1]:8080",
  377. Path: "/",
  378. },
  379. "",
  380. },
  381. // host subcomponent; IPv6 address with zone identifier in RFC 6874
  382. {
  383. "http://[fe80::1%25en0]/", // alphanum zone identifier
  384. &URL{
  385. Scheme: "http",
  386. Host: "[fe80::1%en0]",
  387. Path: "/",
  388. },
  389. "",
  390. },
  391. // host and port subcomponents; IPv6 address with zone identifier in RFC 6874
  392. {
  393. "http://[fe80::1%25en0]:8080/", // alphanum zone identifier
  394. &URL{
  395. Scheme: "http",
  396. Host: "[fe80::1%en0]:8080",
  397. Path: "/",
  398. },
  399. "",
  400. },
  401. // host subcomponent; IPv6 address with zone identifier in RFC 6874
  402. {
  403. "http://[fe80::1%25%65%6e%301-._~]/", // percent-encoded+unreserved zone identifier
  404. &URL{
  405. Scheme: "http",
  406. Host: "[fe80::1%en01-._~]",
  407. Path: "/",
  408. },
  409. "http://[fe80::1%25en01-._~]/",
  410. },
  411. // host and port subcomponents; IPv6 address with zone identifier in RFC 6874
  412. {
  413. "http://[fe80::1%25%65%6e%301-._~]:8080/", // percent-encoded+unreserved zone identifier
  414. &URL{
  415. Scheme: "http",
  416. Host: "[fe80::1%en01-._~]:8080",
  417. Path: "/",
  418. },
  419. "http://[fe80::1%25en01-._~]:8080/",
  420. },
  421. // alternate escapings of path survive round trip
  422. {
  423. "http://rest.rsc.io/foo%2fbar/baz%2Fquux?alt=media",
  424. &URL{
  425. Scheme: "http",
  426. Host: "rest.rsc.io",
  427. Path: "/foo/bar/baz/quux",
  428. RawPath: "/foo%2fbar/baz%2Fquux",
  429. RawQuery: "alt=media",
  430. },
  431. "",
  432. },
  433. // issue 12036
  434. {
  435. "mysql://a,b,c/bar",
  436. &URL{
  437. Scheme: "mysql",
  438. Host: "a,b,c",
  439. Path: "/bar",
  440. },
  441. "",
  442. },
  443. // worst case host, still round trips
  444. {
  445. "scheme://!$&'()*+,;=hello!:1/path",
  446. &URL{
  447. Scheme: "scheme",
  448. Host: "!$&'()*+,;=hello!:1",
  449. Path: "/path",
  450. },
  451. "",
  452. },
  453. // worst case path, still round trips
  454. {
  455. "http://host/!$&'()*+,;=:@[hello]",
  456. &URL{
  457. Scheme: "http",
  458. Host: "host",
  459. Path: "/!$&'()*+,;=:@[hello]",
  460. RawPath: "/!$&'()*+,;=:@[hello]",
  461. },
  462. "",
  463. },
  464. // golang.org/issue/5684
  465. {
  466. "http://example.com/oid/[order_id]",
  467. &URL{
  468. Scheme: "http",
  469. Host: "example.com",
  470. Path: "/oid/[order_id]",
  471. RawPath: "/oid/[order_id]",
  472. },
  473. "",
  474. },
  475. // golang.org/issue/12200 (colon with empty port)
  476. {
  477. "http://192.168.0.2:8080/foo",
  478. &URL{
  479. Scheme: "http",
  480. Host: "192.168.0.2:8080",
  481. Path: "/foo",
  482. },
  483. "",
  484. },
  485. {
  486. "http://192.168.0.2:/foo",
  487. &URL{
  488. Scheme: "http",
  489. Host: "192.168.0.2:",
  490. Path: "/foo",
  491. },
  492. "",
  493. },
  494. {
  495. // Malformed IPv6 but still accepted.
  496. "http://2b01:e34:ef40:7730:8e70:5aff:fefe:edac:8080/foo",
  497. &URL{
  498. Scheme: "http",
  499. Host: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac:8080",
  500. Path: "/foo",
  501. },
  502. "",
  503. },
  504. {
  505. // Malformed IPv6 but still accepted.
  506. "http://2b01:e34:ef40:7730:8e70:5aff:fefe:edac:/foo",
  507. &URL{
  508. Scheme: "http",
  509. Host: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac:",
  510. Path: "/foo",
  511. },
  512. "",
  513. },
  514. {
  515. "http://[2b01:e34:ef40:7730:8e70:5aff:fefe:edac]:8080/foo",
  516. &URL{
  517. Scheme: "http",
  518. Host: "[2b01:e34:ef40:7730:8e70:5aff:fefe:edac]:8080",
  519. Path: "/foo",
  520. },
  521. "",
  522. },
  523. {
  524. "http://[2b01:e34:ef40:7730:8e70:5aff:fefe:edac]:/foo",
  525. &URL{
  526. Scheme: "http",
  527. Host: "[2b01:e34:ef40:7730:8e70:5aff:fefe:edac]:",
  528. Path: "/foo",
  529. },
  530. "",
  531. },
  532. // golang.org/issue/7991 and golang.org/issue/12719 (non-ascii %-encoded in host)
  533. {
  534. "http://hello.世界.com/foo",
  535. &URL{
  536. Scheme: "http",
  537. Host: "hello.世界.com",
  538. Path: "/foo",
  539. },
  540. "http://hello.%E4%B8%96%E7%95%8C.com/foo",
  541. },
  542. {
  543. "http://hello.%e4%b8%96%e7%95%8c.com/foo",
  544. &URL{
  545. Scheme: "http",
  546. Host: "hello.世界.com",
  547. Path: "/foo",
  548. },
  549. "http://hello.%E4%B8%96%E7%95%8C.com/foo",
  550. },
  551. {
  552. "http://hello.%E4%B8%96%E7%95%8C.com/foo",
  553. &URL{
  554. Scheme: "http",
  555. Host: "hello.世界.com",
  556. Path: "/foo",
  557. },
  558. "",
  559. },
  560. // golang.org/issue/10433 (path beginning with //)
  561. {
  562. "http://example.com//foo",
  563. &URL{
  564. Scheme: "http",
  565. Host: "example.com",
  566. Path: "//foo",
  567. },
  568. "",
  569. },
  570. // test that we can reparse the host names we accept.
  571. {
  572. "myscheme://authority<\"hi\">/foo",
  573. &URL{
  574. Scheme: "myscheme",
  575. Host: "authority<\"hi\">",
  576. Path: "/foo",
  577. },
  578. "",
  579. },
  580. // spaces in hosts are disallowed but escaped spaces in IPv6 scope IDs are grudgingly OK.
  581. // This happens on Windows.
  582. // golang.org/issue/14002
  583. {
  584. "tcp://[2020::2020:20:2020:2020%25Windows%20Loves%20Spaces]:2020",
  585. &URL{
  586. Scheme: "tcp",
  587. Host: "[2020::2020:20:2020:2020%Windows Loves Spaces]:2020",
  588. },
  589. "",
  590. },
  591. // test we can roundtrip magnet url
  592. // fix issue https://golang.org/issue/20054
  593. {
  594. "magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a&dn",
  595. &URL{
  596. Scheme: "magnet",
  597. Host: "",
  598. Path: "",
  599. RawQuery: "xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a&dn",
  600. },
  601. "magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a&dn",
  602. },
  603. {
  604. "mailto:?subject=hi",
  605. &URL{
  606. Scheme: "mailto",
  607. Host: "",
  608. Path: "",
  609. RawQuery: "subject=hi",
  610. },
  611. "mailto:?subject=hi",
  612. },
  613. }
  614. // more useful string for debugging than fmt's struct printer
  615. func ufmt(u *URL) string {
  616. var user, pass any
  617. if u.User != nil {
  618. user = u.User.Username()
  619. if p, ok := u.User.Password(); ok {
  620. pass = p
  621. }
  622. }
  623. return fmt.Sprintf("opaque=%q, scheme=%q, user=%#v, pass=%#v, host=%q, path=%q, rawpath=%q, rawq=%q, frag=%q, rawfrag=%q, forcequery=%v",
  624. u.Opaque, u.Scheme, user, pass, u.Host, u.Path, u.RawPath, u.RawQuery, u.Fragment, u.RawFragment, u.ForceQuery)
  625. }
  626. func BenchmarkString(b *testing.B) {
  627. b.StopTimer()
  628. b.ReportAllocs()
  629. for _, tt := range urltests {
  630. u, err := Parse(tt.in)
  631. if err != nil {
  632. b.Errorf("Parse(%q) returned error %s", tt.in, err)
  633. continue
  634. }
  635. if tt.roundtrip == "" {
  636. continue
  637. }
  638. b.StartTimer()
  639. var g string
  640. for i := 0; i < b.N; i++ {
  641. g = u.String()
  642. }
  643. b.StopTimer()
  644. if w := tt.roundtrip; b.N > 0 && g != w {
  645. b.Errorf("Parse(%q).String() == %q, want %q", tt.in, g, w)
  646. }
  647. }
  648. }
  649. func TestParse(t *testing.T) {
  650. for _, tt := range urltests {
  651. u, err := Parse(tt.in)
  652. if err != nil {
  653. t.Errorf("Parse(%q) returned error %v", tt.in, err)
  654. continue
  655. }
  656. if !reflect.DeepEqual(u, tt.out) {
  657. t.Errorf("Parse(%q):\n\tgot %v\n\twant %v\n", tt.in, ufmt(u), ufmt(tt.out))
  658. }
  659. }
  660. }
  661. const pathThatLooksSchemeRelative = "//not.a.user@not.a.host/just/a/path"
  662. var parseRequestURLTests = []struct {
  663. url string
  664. expectedValid bool
  665. }{
  666. {"http://foo.com", true},
  667. {"http://foo.com/", true},
  668. {"http://foo.com/path", true},
  669. {"/", true},
  670. {pathThatLooksSchemeRelative, true},
  671. {"//not.a.user@%66%6f%6f.com/just/a/path/also", true},
  672. {"*", true},
  673. {"http://192.168.0.1/", true},
  674. {"http://192.168.0.1:8080/", true},
  675. {"http://[fe80::1]/", true},
  676. {"http://[fe80::1]:8080/", true},
  677. // Tests exercising RFC 6874 compliance:
  678. {"http://[fe80::1%25en0]/", true}, // with alphanum zone identifier
  679. {"http://[fe80::1%25en0]:8080/", true}, // with alphanum zone identifier
  680. {"http://[fe80::1%25%65%6e%301-._~]/", true}, // with percent-encoded+unreserved zone identifier
  681. {"http://[fe80::1%25%65%6e%301-._~]:8080/", true}, // with percent-encoded+unreserved zone identifier
  682. {"foo.html", false},
  683. {"../dir/", false},
  684. {" http://foo.com", false},
  685. {"http://192.168.0.%31/", false},
  686. {"http://192.168.0.%31:8080/", false},
  687. {"http://[fe80::%31]/", false},
  688. {"http://[fe80::%31]:8080/", false},
  689. {"http://[fe80::%31%25en0]/", false},
  690. {"http://[fe80::%31%25en0]:8080/", false},
  691. // These two cases are valid as textual representations as
  692. // described in RFC 4007, but are not valid as address
  693. // literals with IPv6 zone identifiers in URIs as described in
  694. // RFC 6874.
  695. {"http://[fe80::1%en0]/", false},
  696. {"http://[fe80::1%en0]:8080/", false},
  697. }
  698. func TestParseRequestURI(t *testing.T) {
  699. for _, test := range parseRequestURLTests {
  700. _, err := ParseRequestURI(test.url)
  701. if test.expectedValid && err != nil {
  702. t.Errorf("ParseRequestURI(%q) gave err %v; want no error", test.url, err)
  703. } else if !test.expectedValid && err == nil {
  704. t.Errorf("ParseRequestURI(%q) gave nil error; want some error", test.url)
  705. }
  706. }
  707. url, err := ParseRequestURI(pathThatLooksSchemeRelative)
  708. if err != nil {
  709. t.Fatalf("Unexpected error %v", err)
  710. }
  711. if url.Path != pathThatLooksSchemeRelative {
  712. t.Errorf("ParseRequestURI path:\ngot %q\nwant %q", url.Path, pathThatLooksSchemeRelative)
  713. }
  714. }
  715. var stringURLTests = []struct {
  716. url URL
  717. want string
  718. }{
  719. // No leading slash on path should prepend slash on String() call
  720. {
  721. url: URL{
  722. Scheme: "http",
  723. Host: "www.google.com",
  724. Path: "search",
  725. },
  726. want: "http://www.google.com/search",
  727. },
  728. // Relative path with first element containing ":" should be prepended with "./", golang.org/issue/17184
  729. {
  730. url: URL{
  731. Path: "this:that",
  732. },
  733. want: "./this:that",
  734. },
  735. // Relative path with second element containing ":" should not be prepended with "./"
  736. {
  737. url: URL{
  738. Path: "here/this:that",
  739. },
  740. want: "here/this:that",
  741. },
  742. // Non-relative path with first element containing ":" should not be prepended with "./"
  743. {
  744. url: URL{
  745. Scheme: "http",
  746. Host: "www.google.com",
  747. Path: "this:that",
  748. },
  749. want: "http://www.google.com/this:that",
  750. },
  751. }
  752. func TestURLString(t *testing.T) {
  753. for _, tt := range urltests {
  754. u, err := Parse(tt.in)
  755. if err != nil {
  756. t.Errorf("Parse(%q) returned error %s", tt.in, err)
  757. continue
  758. }
  759. expected := tt.in
  760. if tt.roundtrip != "" {
  761. expected = tt.roundtrip
  762. }
  763. s := u.String()
  764. if s != expected {
  765. t.Errorf("Parse(%q).String() == %q (expected %q)", tt.in, s, expected)
  766. }
  767. }
  768. for _, tt := range stringURLTests {
  769. if got := tt.url.String(); got != tt.want {
  770. t.Errorf("%+v.String() = %q; want %q", tt.url, got, tt.want)
  771. }
  772. }
  773. }
  774. func TestURLRedacted(t *testing.T) {
  775. cases := []struct {
  776. name string
  777. url *URL
  778. want string
  779. }{
  780. {
  781. name: "non-blank Password",
  782. url: &URL{
  783. Scheme: "http",
  784. Host: "host.tld",
  785. Path: "this:that",
  786. User: UserPassword("user", "password"),
  787. },
  788. want: "http://user:xxxxx@host.tld/this:that",
  789. },
  790. {
  791. name: "blank Password",
  792. url: &URL{
  793. Scheme: "http",
  794. Host: "host.tld",
  795. Path: "this:that",
  796. User: User("user"),
  797. },
  798. want: "http://user@host.tld/this:that",
  799. },
  800. {
  801. name: "nil User",
  802. url: &URL{
  803. Scheme: "http",
  804. Host: "host.tld",
  805. Path: "this:that",
  806. User: UserPassword("", "password"),
  807. },
  808. want: "http://:xxxxx@host.tld/this:that",
  809. },
  810. {
  811. name: "blank Username, blank Password",
  812. url: &URL{
  813. Scheme: "http",
  814. Host: "host.tld",
  815. Path: "this:that",
  816. },
  817. want: "http://host.tld/this:that",
  818. },
  819. {
  820. name: "empty URL",
  821. url: &URL{},
  822. want: "",
  823. },
  824. {
  825. name: "nil URL",
  826. url: nil,
  827. want: "",
  828. },
  829. }
  830. for _, tt := range cases {
  831. t := t
  832. t.Run(tt.name, func(t *testing.T) {
  833. if g, w := tt.url.Redacted(), tt.want; g != w {
  834. t.Fatalf("got: %q\nwant: %q", g, w)
  835. }
  836. })
  837. }
  838. }
  839. type EscapeTest struct {
  840. in string
  841. out string
  842. err error
  843. }
  844. var unescapeTests = []EscapeTest{
  845. {
  846. "",
  847. "",
  848. nil,
  849. },
  850. {
  851. "abc",
  852. "abc",
  853. nil,
  854. },
  855. {
  856. "1%41",
  857. "1A",
  858. nil,
  859. },
  860. {
  861. "1%41%42%43",
  862. "1ABC",
  863. nil,
  864. },
  865. {
  866. "%4a",
  867. "J",
  868. nil,
  869. },
  870. {
  871. "%6F",
  872. "o",
  873. nil,
  874. },
  875. {
  876. "%", // not enough characters after %
  877. "",
  878. EscapeError("%"),
  879. },
  880. {
  881. "%a", // not enough characters after %
  882. "",
  883. EscapeError("%a"),
  884. },
  885. {
  886. "%1", // not enough characters after %
  887. "",
  888. EscapeError("%1"),
  889. },
  890. {
  891. "123%45%6", // not enough characters after %
  892. "",
  893. EscapeError("%6"),
  894. },
  895. {
  896. "%zzzzz", // invalid hex digits
  897. "",
  898. EscapeError("%zz"),
  899. },
  900. {
  901. "a+b",
  902. "a b",
  903. nil,
  904. },
  905. {
  906. "a%20b",
  907. "a b",
  908. nil,
  909. },
  910. }
  911. func TestUnescape(t *testing.T) {
  912. for _, tt := range unescapeTests {
  913. actual, err := QueryUnescape(tt.in)
  914. if actual != tt.out || (err != nil) != (tt.err != nil) {
  915. t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", tt.in, actual, err, tt.out, tt.err)
  916. }
  917. in := tt.in
  918. out := tt.out
  919. if strings.Contains(tt.in, "+") {
  920. in = strings.ReplaceAll(tt.in, "+", "%20")
  921. actual, err := PathUnescape(in)
  922. if actual != tt.out || (err != nil) != (tt.err != nil) {
  923. t.Errorf("PathUnescape(%q) = %q, %s; want %q, %s", in, actual, err, tt.out, tt.err)
  924. }
  925. if tt.err == nil {
  926. s, err := QueryUnescape(strings.ReplaceAll(tt.in, "+", "XXX"))
  927. if err != nil {
  928. continue
  929. }
  930. in = tt.in
  931. out = strings.ReplaceAll(s, "XXX", "+")
  932. }
  933. }
  934. actual, err = PathUnescape(in)
  935. if actual != out || (err != nil) != (tt.err != nil) {
  936. t.Errorf("PathUnescape(%q) = %q, %s; want %q, %s", in, actual, err, out, tt.err)
  937. }
  938. }
  939. }
  940. var queryEscapeTests = []EscapeTest{
  941. {
  942. "",
  943. "",
  944. nil,
  945. },
  946. {
  947. "abc",
  948. "abc",
  949. nil,
  950. },
  951. {
  952. "one two",
  953. "one+two",
  954. nil,
  955. },
  956. {
  957. "10%",
  958. "10%25",
  959. nil,
  960. },
  961. {
  962. " ?&=#+%!<>#\"{}|\\^[]`☺\t:/@$'()*,;",
  963. "+%3F%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09%3A%2F%40%24%27%28%29%2A%2C%3B",
  964. nil,
  965. },
  966. }
  967. func TestQueryEscape(t *testing.T) {
  968. for _, tt := range queryEscapeTests {
  969. actual := QueryEscape(tt.in)
  970. if tt.out != actual {
  971. t.Errorf("QueryEscape(%q) = %q, want %q", tt.in, actual, tt.out)
  972. }
  973. // for bonus points, verify that escape:unescape is an identity.
  974. roundtrip, err := QueryUnescape(actual)
  975. if roundtrip != tt.in || err != nil {
  976. t.Errorf("QueryUnescape(%q) = %q, %s; want %q, %s", actual, roundtrip, err, tt.in, "[no error]")
  977. }
  978. }
  979. }
  980. var pathEscapeTests = []EscapeTest{
  981. {
  982. "",
  983. "",
  984. nil,
  985. },
  986. {
  987. "abc",
  988. "abc",
  989. nil,
  990. },
  991. {
  992. "abc+def",
  993. "abc+def",
  994. nil,
  995. },
  996. {
  997. "a/b",
  998. "a%2Fb",
  999. nil,
  1000. },
  1001. {
  1002. "one two",
  1003. "one%20two",
  1004. nil,
  1005. },
  1006. {
  1007. "10%",
  1008. "10%25",
  1009. nil,
  1010. },
  1011. {
  1012. " ?&=#+%!<>#\"{}|\\^[]`☺\t:/@$'()*,;",
  1013. "%20%3F&=%23+%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%E2%98%BA%09:%2F@$%27%28%29%2A%2C%3B",
  1014. nil,
  1015. },
  1016. }
  1017. func TestPathEscape(t *testing.T) {
  1018. for _, tt := range pathEscapeTests {
  1019. actual := PathEscape(tt.in)
  1020. if tt.out != actual {
  1021. t.Errorf("PathEscape(%q) = %q, want %q", tt.in, actual, tt.out)
  1022. }
  1023. // for bonus points, verify that escape:unescape is an identity.
  1024. roundtrip, err := PathUnescape(actual)
  1025. if roundtrip != tt.in || err != nil {
  1026. t.Errorf("PathUnescape(%q) = %q, %s; want %q, %s", actual, roundtrip, err, tt.in, "[no error]")
  1027. }
  1028. }
  1029. }
  1030. //var userinfoTests = []UserinfoTest{
  1031. // {"user", "password", "user:password"},
  1032. // {"foo:bar", "~!@#$%^&*()_+{}|[]\\-=`:;'\"<>?,./",
  1033. // "foo%3Abar:~!%40%23$%25%5E&*()_+%7B%7D%7C%5B%5D%5C-=%60%3A;'%22%3C%3E?,.%2F"},
  1034. //}
  1035. type EncodeQueryTest struct {
  1036. m Values
  1037. expected string
  1038. }
  1039. var encodeQueryTests = []EncodeQueryTest{
  1040. {nil, ""},
  1041. {Values{"q": {"puppies"}, "oe": {"utf8"}}, "oe=utf8&q=puppies"},
  1042. {Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7"},
  1043. {Values{
  1044. "a": {"a1", "a2", "a3"},
  1045. "b": {"b1", "b2", "b3"},
  1046. "c": {"c1", "c2", "c3"},
  1047. }, "a=a1&a=a2&a=a3&b=b1&b=b2&b=b3&c=c1&c=c2&c=c3"},
  1048. }
  1049. func TestEncodeQuery(t *testing.T) {
  1050. for _, tt := range encodeQueryTests {
  1051. if q := tt.m.Encode(); q != tt.expected {
  1052. t.Errorf(`EncodeQuery(%+v) = %q, want %q`, tt.m, q, tt.expected)
  1053. }
  1054. }
  1055. }
  1056. var resolvePathTests = []struct {
  1057. base, ref, expected string
  1058. }{
  1059. {"a/b", ".", "/a/"},
  1060. {"a/b", "c", "/a/c"},
  1061. {"a/b", "..", "/"},
  1062. {"a/", "..", "/"},
  1063. {"a/", "../..", "/"},
  1064. {"a/b/c", "..", "/a/"},
  1065. {"a/b/c", "../d", "/a/d"},
  1066. {"a/b/c", ".././d", "/a/d"},
  1067. {"a/b", "./..", "/"},
  1068. {"a/./b", ".", "/a/"},
  1069. {"a/../", ".", "/"},
  1070. {"a/.././b", "c", "/c"},
  1071. }
  1072. func TestResolvePath(t *testing.T) {
  1073. for _, test := range resolvePathTests {
  1074. got := resolvePath(test.base, test.ref)
  1075. if got != test.expected {
  1076. t.Errorf("For %q + %q got %q; expected %q", test.base, test.ref, got, test.expected)
  1077. }
  1078. }
  1079. }
  1080. func BenchmarkResolvePath(b *testing.B) {
  1081. b.ResetTimer()
  1082. b.ReportAllocs()
  1083. for i := 0; i < b.N; i++ {
  1084. resolvePath("a/b/c", ".././d")
  1085. }
  1086. }
  1087. var resolveReferenceTests = []struct {
  1088. base, rel, expected string
  1089. }{
  1090. // Absolute URL references
  1091. {"http://foo.com?a=b", "https://bar.com/", "https://bar.com/"},
  1092. {"http://foo.com/", "https://bar.com/?a=b", "https://bar.com/?a=b"},
  1093. {"http://foo.com/", "https://bar.com/?", "https://bar.com/?"},
  1094. {"http://foo.com/bar", "mailto:foo@example.com", "mailto:foo@example.com"},
  1095. // Path-absolute references
  1096. {"http://foo.com/bar", "/baz", "http://foo.com/baz"},
  1097. {"http://foo.com/bar?a=b#f", "/baz", "http://foo.com/baz"},
  1098. {"http://foo.com/bar?a=b", "/baz?", "http://foo.com/baz?"},
  1099. {"http://foo.com/bar?a=b", "/baz?c=d", "http://foo.com/baz?c=d"},
  1100. // Multiple slashes
  1101. {"http://foo.com/bar", "http://foo.com//baz", "http://foo.com//baz"},
  1102. {"http://foo.com/bar", "http://foo.com///baz/quux", "http://foo.com///baz/quux"},
  1103. // Scheme-relative
  1104. {"https://foo.com/bar?a=b", "//bar.com/quux", "https://bar.com/quux"},
  1105. // Path-relative references:
  1106. // ... current directory
  1107. {"http://foo.com", ".", "http://foo.com/"},
  1108. {"http://foo.com/bar", ".", "http://foo.com/"},
  1109. {"http://foo.com/bar/", ".", "http://foo.com/bar/"},
  1110. // ... going down
  1111. {"http://foo.com", "bar", "http://foo.com/bar"},
  1112. {"http://foo.com/", "bar", "http://foo.com/bar"},
  1113. {"http://foo.com/bar/baz", "quux", "http://foo.com/bar/quux"},
  1114. // ... going up
  1115. {"http://foo.com/bar/baz", "../quux", "http://foo.com/quux"},
  1116. {"http://foo.com/bar/baz", "../../../../../quux", "http://foo.com/quux"},
  1117. {"http://foo.com/bar", "..", "http://foo.com/"},
  1118. {"http://foo.com/bar/baz", "./..", "http://foo.com/"},
  1119. // ".." in the middle (issue 3560)
  1120. {"http://foo.com/bar/baz", "quux/dotdot/../tail", "http://foo.com/bar/quux/tail"},
  1121. {"http://foo.com/bar/baz", "quux/./dotdot/../tail", "http://foo.com/bar/quux/tail"},
  1122. {"http://foo.com/bar/baz", "quux/./dotdot/.././tail", "http://foo.com/bar/quux/tail"},
  1123. {"http://foo.com/bar/baz", "quux/./dotdot/./../tail", "http://foo.com/bar/quux/tail"},
  1124. {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/././../../tail", "http://foo.com/bar/quux/tail"},
  1125. {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/./.././../tail", "http://foo.com/bar/quux/tail"},
  1126. {"http://foo.com/bar/baz", "quux/./dotdot/dotdot/dotdot/./../../.././././tail", "http://foo.com/bar/quux/tail"},
  1127. {"http://foo.com/bar/baz", "quux/./dotdot/../dotdot/../dot/./tail/..", "http://foo.com/bar/quux/dot/"},
  1128. // Remove any dot-segments prior to forming the target URI.
  1129. // https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
  1130. {"http://foo.com/dot/./dotdot/../foo/bar", "../baz", "http://foo.com/dot/baz"},
  1131. // Triple dot isn't special
  1132. {"http://foo.com/bar", "...", "http://foo.com/..."},
  1133. // Fragment
  1134. {"http://foo.com/bar", ".#frag", "http://foo.com/#frag"},
  1135. {"http://example.org/", "#!$&%27()*+,;=", "http://example.org/#!$&%27()*+,;="},
  1136. // Paths with escaping (issue 16947).
  1137. {"http://foo.com/foo%2fbar/", "../baz", "http://foo.com/baz"},
  1138. {"http://foo.com/1/2%2f/3%2f4/5", "../../a/b/c", "http://foo.com/1/a/b/c"},
  1139. {"http://foo.com/1/2/3", "./a%2f../../b/..%2fc", "http://foo.com/1/2/b/..%2fc"},
  1140. {"http://foo.com/1/2%2f/3%2f4/5", "./a%2f../b/../c", "http://foo.com/1/2%2f/3%2f4/a%2f../c"},
  1141. {"http://foo.com/foo%20bar/", "../baz", "http://foo.com/baz"},
  1142. {"http://foo.com/foo", "../bar%2fbaz", "http://foo.com/bar%2fbaz"},
  1143. {"http://foo.com/foo%2dbar/", "./baz-quux", "http://foo.com/foo%2dbar/baz-quux"},
  1144. // RFC 3986: Normal Examples
  1145. // https://datatracker.ietf.org/doc/html/rfc3986#section-5.4.1
  1146. {"http://a/b/c/d;p?q", "g:h", "g:h"},
  1147. {"http://a/b/c/d;p?q", "g", "http://a/b/c/g"},
  1148. {"http://a/b/c/d;p?q", "./g", "http://a/b/c/g"},
  1149. {"http://a/b/c/d;p?q", "g/", "http://a/b/c/g/"},
  1150. {"http://a/b/c/d;p?q", "/g", "http://a/g"},
  1151. {"http://a/b/c/d;p?q", "//g", "http://g"},
  1152. {"http://a/b/c/d;p?q", "?y", "http://a/b/c/d;p?y"},
  1153. {"http://a/b/c/d;p?q", "g?y", "http://a/b/c/g?y"},
  1154. {"http://a/b/c/d;p?q", "#s", "http://a/b/c/d;p?q#s"},
  1155. {"http://a/b/c/d;p?q", "g#s", "http://a/b/c/g#s"},
  1156. {"http://a/b/c/d;p?q", "g?y#s", "http://a/b/c/g?y#s"},
  1157. {"http://a/b/c/d;p?q", ";x", "http://a/b/c/;x"},
  1158. {"http://a/b/c/d;p?q", "g;x", "http://a/b/c/g;x"},
  1159. {"http://a/b/c/d;p?q", "g;x?y#s", "http://a/b/c/g;x?y#s"},
  1160. {"http://a/b/c/d;p?q", "", "http://a/b/c/d;p?q"},
  1161. {"http://a/b/c/d;p?q", ".", "http://a/b/c/"},
  1162. {"http://a/b/c/d;p?q", "./", "http://a/b/c/"},
  1163. {"http://a/b/c/d;p?q", "..", "http://a/b/"},
  1164. {"http://a/b/c/d;p?q", "../", "http://a/b/"},
  1165. {"http://a/b/c/d;p?q", "../g", "http://a/b/g"},
  1166. {"http://a/b/c/d;p?q", "../..", "http://a/"},
  1167. {"http://a/b/c/d;p?q", "../../", "http://a/"},
  1168. {"http://a/b/c/d;p?q", "../../g", "http://a/g"},
  1169. // RFC 3986: Abnormal Examples
  1170. // https://datatracker.ietf.org/doc/html/rfc3986#section-5.4.2
  1171. {"http://a/b/c/d;p?q", "../../../g", "http://a/g"},
  1172. {"http://a/b/c/d;p?q", "../../../../g", "http://a/g"},
  1173. {"http://a/b/c/d;p?q", "/./g", "http://a/g"},
  1174. {"http://a/b/c/d;p?q", "/../g", "http://a/g"},
  1175. {"http://a/b/c/d;p?q", "g.", "http://a/b/c/g."},
  1176. {"http://a/b/c/d;p?q", ".g", "http://a/b/c/.g"},
  1177. {"http://a/b/c/d;p?q", "g..", "http://a/b/c/g.."},
  1178. {"http://a/b/c/d;p?q", "..g", "http://a/b/c/..g"},
  1179. {"http://a/b/c/d;p?q", "./../g", "http://a/b/g"},
  1180. {"http://a/b/c/d;p?q", "./g/.", "http://a/b/c/g/"},
  1181. {"http://a/b/c/d;p?q", "g/./h", "http://a/b/c/g/h"},
  1182. {"http://a/b/c/d;p?q", "g/../h", "http://a/b/c/h"},
  1183. {"http://a/b/c/d;p?q", "g;x=1/./y", "http://a/b/c/g;x=1/y"},
  1184. {"http://a/b/c/d;p?q", "g;x=1/../y", "http://a/b/c/y"},
  1185. {"http://a/b/c/d;p?q", "g?y/./x", "http://a/b/c/g?y/./x"},
  1186. {"http://a/b/c/d;p?q", "g?y/../x", "http://a/b/c/g?y/../x"},
  1187. {"http://a/b/c/d;p?q", "g#s/./x", "http://a/b/c/g#s/./x"},
  1188. {"http://a/b/c/d;p?q", "g#s/../x", "http://a/b/c/g#s/../x"},
  1189. // Extras.
  1190. {"https://a/b/c/d;p?q", "//g?q", "https://g?q"},
  1191. {"https://a/b/c/d;p?q", "//g#s", "https://g#s"},
  1192. {"https://a/b/c/d;p?q", "//g/d/e/f?y#s", "https://g/d/e/f?y#s"},
  1193. {"https://a/b/c/d;p#s", "?y", "https://a/b/c/d;p?y"},
  1194. {"https://a/b/c/d;p?q#s", "?y", "https://a/b/c/d;p?y"},
  1195. // Empty path and query but with ForceQuery (issue 46033).
  1196. {"https://a/b/c/d;p?q#s", "?", "https://a/b/c/d;p?"},
  1197. }
  1198. func TestResolveReference(t *testing.T) {
  1199. mustParse := func(url string) *URL {
  1200. u, err := Parse(url)
  1201. if err != nil {
  1202. t.Fatalf("Parse(%q) got err %v", url, err)
  1203. }
  1204. return u
  1205. }
  1206. opaque := &URL{Scheme: "scheme", Opaque: "opaque"}
  1207. for _, test := range resolveReferenceTests {
  1208. base := mustParse(test.base)
  1209. rel := mustParse(test.rel)
  1210. url := base.ResolveReference(rel)
  1211. if got := url.String(); got != test.expected {
  1212. t.Errorf("URL(%q).ResolveReference(%q)\ngot %q\nwant %q", test.base, test.rel, got, test.expected)
  1213. }
  1214. // Ensure that new instances are returned.
  1215. if base == url {
  1216. t.Errorf("Expected URL.ResolveReference to return new URL instance.")
  1217. }
  1218. // Test the convenience wrapper too.
  1219. url, err := base.Parse(test.rel)
  1220. if err != nil {
  1221. t.Errorf("URL(%q).Parse(%q) failed: %v", test.base, test.rel, err)
  1222. } else if got := url.String(); got != test.expected {
  1223. t.Errorf("URL(%q).Parse(%q)\ngot %q\nwant %q", test.base, test.rel, got, test.expected)
  1224. } else if base == url {
  1225. // Ensure that new instances are returned for the wrapper too.
  1226. t.Errorf("Expected URL.Parse to return new URL instance.")
  1227. }
  1228. // Ensure Opaque resets the URL.
  1229. url = base.ResolveReference(opaque)
  1230. if *url != *opaque {
  1231. t.Errorf("ResolveReference failed to resolve opaque URL:\ngot %#v\nwant %#v", url, opaque)
  1232. }
  1233. // Test the convenience wrapper with an opaque URL too.
  1234. url, err = base.Parse("scheme:opaque")
  1235. if err != nil {
  1236. t.Errorf(`URL(%q).Parse("scheme:opaque") failed: %v`, test.base, err)
  1237. } else if *url != *opaque {
  1238. t.Errorf("Parse failed to resolve opaque URL:\ngot %#v\nwant %#v", opaque, url)
  1239. } else if base == url {
  1240. // Ensure that new instances are returned, again.
  1241. t.Errorf("Expected URL.Parse to return new URL instance.")
  1242. }
  1243. }
  1244. }
  1245. func TestQueryValues(t *testing.T) {
  1246. u, _ := Parse("http://x.com?foo=bar&bar=1&bar=2&baz")
  1247. v := u.Query()
  1248. if len(v) != 3 {
  1249. t.Errorf("got %d keys in Query values, want 3", len(v))
  1250. }
  1251. if g, e := v.Get("foo"), "bar"; g != e {
  1252. t.Errorf("Get(foo) = %q, want %q", g, e)
  1253. }
  1254. // Case sensitive:
  1255. if g, e := v.Get("Foo"), ""; g != e {
  1256. t.Errorf("Get(Foo) = %q, want %q", g, e)
  1257. }
  1258. if g, e := v.Get("bar"), "1"; g != e {
  1259. t.Errorf("Get(bar) = %q, want %q", g, e)
  1260. }
  1261. if g, e := v.Get("baz"), ""; g != e {
  1262. t.Errorf("Get(baz) = %q, want %q", g, e)
  1263. }
  1264. if h, e := v.Has("foo"), true; h != e {
  1265. t.Errorf("Has(foo) = %t, want %t", h, e)
  1266. }
  1267. if h, e := v.Has("bar"), true; h != e {
  1268. t.Errorf("Has(bar) = %t, want %t", h, e)
  1269. }
  1270. if h, e := v.Has("baz"), true; h != e {
  1271. t.Errorf("Has(baz) = %t, want %t", h, e)
  1272. }
  1273. if h, e := v.Has("noexist"), false; h != e {
  1274. t.Errorf("Has(noexist) = %t, want %t", h, e)
  1275. }
  1276. v.Del("bar")
  1277. if g, e := v.Get("bar"), ""; g != e {
  1278. t.Errorf("second Get(bar) = %q, want %q", g, e)
  1279. }
  1280. }
  1281. type parseTest struct {
  1282. query string
  1283. out Values
  1284. ok bool
  1285. }
  1286. var parseTests = []parseTest{
  1287. {
  1288. query: "a=1",
  1289. out: Values{"a": []string{"1"}},
  1290. ok: true,
  1291. },
  1292. {
  1293. query: "a=1&b=2",
  1294. out: Values{"a": []string{"1"}, "b": []string{"2"}},
  1295. ok: true,
  1296. },
  1297. {
  1298. query: "a=1&a=2&a=banana",
  1299. out: Values{"a": []string{"1", "2", "banana"}},
  1300. ok: true,
  1301. },
  1302. {
  1303. query: "ascii=%3Ckey%3A+0x90%3E",
  1304. out: Values{"ascii": []string{"<key: 0x90>"}},
  1305. ok: true,
  1306. }, {
  1307. query: "a=1;b=2",
  1308. out: Values{},
  1309. ok: false,
  1310. }, {
  1311. query: "a;b=1",
  1312. out: Values{},
  1313. ok: false,
  1314. }, {
  1315. query: "a=%3B", // hex encoding for semicolon
  1316. out: Values{"a": []string{";"}},
  1317. ok: true,
  1318. },
  1319. {
  1320. query: "a%3Bb=1",
  1321. out: Values{"a;b": []string{"1"}},
  1322. ok: true,
  1323. },
  1324. {
  1325. query: "a=1&a=2;a=banana",
  1326. out: Values{"a": []string{"1"}},
  1327. ok: false,
  1328. },
  1329. {
  1330. query: "a;b&c=1",
  1331. out: Values{"c": []string{"1"}},
  1332. ok: false,
  1333. },
  1334. {
  1335. query: "a=1&b=2;a=3&c=4",
  1336. out: Values{"a": []string{"1"}, "c": []string{"4"}},
  1337. ok: false,
  1338. },
  1339. {
  1340. query: "a=1&b=2;c=3",
  1341. out: Values{"a": []string{"1"}},
  1342. ok: false,
  1343. },
  1344. {
  1345. query: ";",
  1346. out: Values{},
  1347. ok: false,
  1348. },
  1349. {
  1350. query: "a=1;",
  1351. out: Values{},
  1352. ok: false,
  1353. },
  1354. {
  1355. query: "a=1&;",
  1356. out: Values{"a": []string{"1"}},
  1357. ok: false,
  1358. },
  1359. {
  1360. query: ";a=1&b=2",
  1361. out: Values{"b": []string{"2"}},
  1362. ok: false,
  1363. },
  1364. {
  1365. query: "a=1&b=2;",
  1366. out: Values{"a": []string{"1"}},
  1367. ok: false,
  1368. },
  1369. }
  1370. func TestParseQuery(t *testing.T) {
  1371. for _, test := range parseTests {
  1372. t.Run(test.query, func(t *testing.T) {
  1373. form, err := ParseQuery(test.query)
  1374. if test.ok != (err == nil) {
  1375. want := "<error>"
  1376. if test.ok {
  1377. want = "<nil>"
  1378. }
  1379. t.Errorf("Unexpected error: %v, want %v", err, want)
  1380. }
  1381. if len(form) != len(test.out) {
  1382. t.Errorf("len(form) = %d, want %d", len(form), len(test.out))
  1383. }
  1384. for k, evs := range test.out {
  1385. vs, ok := form[k]
  1386. if !ok {
  1387. t.Errorf("Missing key %q", k)
  1388. continue
  1389. }
  1390. if len(vs) != len(evs) {
  1391. t.Errorf("len(form[%q]) = %d, want %d", k, len(vs), len(evs))
  1392. continue
  1393. }
  1394. for j, ev := range evs {
  1395. if v := vs[j]; v != ev {
  1396. t.Errorf("form[%q][%d] = %q, want %q", k, j, v, ev)
  1397. }
  1398. }
  1399. }
  1400. })
  1401. }
  1402. }
  1403. type RequestURITest struct {
  1404. url *URL
  1405. out string
  1406. }
  1407. var requritests = []RequestURITest{
  1408. {
  1409. &URL{
  1410. Scheme: "http",
  1411. Host: "example.com",
  1412. Path: "",
  1413. },
  1414. "/",
  1415. },
  1416. {
  1417. &URL{
  1418. Scheme: "http",
  1419. Host: "example.com",
  1420. Path: "/a b",
  1421. },
  1422. "/a%20b",
  1423. },
  1424. // golang.org/issue/4860 variant 1
  1425. {
  1426. &URL{
  1427. Scheme: "http",
  1428. Host: "example.com",
  1429. Opaque: "/%2F/%2F/",
  1430. },
  1431. "/%2F/%2F/",
  1432. },
  1433. // golang.org/issue/4860 variant 2
  1434. {
  1435. &URL{
  1436. Scheme: "http",
  1437. Host: "example.com",
  1438. Opaque: "//other.example.com/%2F/%2F/",
  1439. },
  1440. "http://other.example.com/%2F/%2F/",
  1441. },
  1442. // better fix for issue 4860
  1443. {
  1444. &URL{
  1445. Scheme: "http",
  1446. Host: "example.com",
  1447. Path: "/////",
  1448. RawPath: "/%2F/%2F/",
  1449. },
  1450. "/%2F/%2F/",
  1451. },
  1452. {
  1453. &URL{
  1454. Scheme: "http",
  1455. Host: "example.com",
  1456. Path: "/////",
  1457. RawPath: "/WRONG/", // ignored because doesn't match Path
  1458. },
  1459. "/////",
  1460. },
  1461. {
  1462. &URL{
  1463. Scheme: "http",
  1464. Host: "example.com",
  1465. Path: "/a b",
  1466. RawQuery: "q=go+language",
  1467. },
  1468. "/a%20b?q=go+language",
  1469. },
  1470. {
  1471. &URL{
  1472. Scheme: "http",
  1473. Host: "example.com",
  1474. Path: "/a b",
  1475. RawPath: "/a b", // ignored because invalid
  1476. RawQuery: "q=go+language",
  1477. },
  1478. "/a%20b?q=go+language",
  1479. },
  1480. {
  1481. &URL{
  1482. Scheme: "http",
  1483. Host: "example.com",
  1484. Path: "/a?b",
  1485. RawPath: "/a?b", // ignored because invalid
  1486. RawQuery: "q=go+language",
  1487. },
  1488. "/a%3Fb?q=go+language",
  1489. },
  1490. {
  1491. &URL{
  1492. Scheme: "myschema",
  1493. Opaque: "opaque",
  1494. },
  1495. "opaque",
  1496. },
  1497. {
  1498. &URL{
  1499. Scheme: "myschema",
  1500. Opaque: "opaque",
  1501. RawQuery: "q=go+language",
  1502. },
  1503. "opaque?q=go+language",
  1504. },
  1505. {
  1506. &URL{
  1507. Scheme: "http",
  1508. Host: "example.com",
  1509. Path: "//foo",
  1510. },
  1511. "//foo",
  1512. },
  1513. {
  1514. &URL{
  1515. Scheme: "http",
  1516. Host: "example.com",
  1517. Path: "/foo",
  1518. ForceQuery: true,
  1519. },
  1520. "/foo?",
  1521. },
  1522. }
  1523. func TestRequestURI(t *testing.T) {
  1524. for _, tt := range requritests {
  1525. s := tt.url.RequestURI()
  1526. if s != tt.out {
  1527. t.Errorf("%#v.RequestURI() == %q (expected %q)", tt.url, s, tt.out)
  1528. }
  1529. }
  1530. }
  1531. func TestParseFailure(t *testing.T) {
  1532. // Test that the first parse error is returned.
  1533. const url = "%gh&%ij"
  1534. _, err := ParseQuery(url)
  1535. errStr := fmt.Sprint(err)
  1536. if !strings.Contains(errStr, "%gh") {
  1537. t.Errorf(`ParseQuery(%q) returned error %q, want something containing %q"`, url, errStr, "%gh")
  1538. }
  1539. }
  1540. func TestParseErrors(t *testing.T) {
  1541. tests := []struct {
  1542. in string
  1543. wantErr bool
  1544. }{
  1545. {"http://[::1]", false},
  1546. {"http://[::1]:80", false},
  1547. {"http://[::1]:namedport", true}, // rfc3986 3.2.3
  1548. {"http://x:namedport", true}, // rfc3986 3.2.3
  1549. {"http://[::1]/", false},
  1550. {"http://[::1]a", true},
  1551. {"http://[::1]%23", true},
  1552. {"http://[::1%25en0]", false}, // valid zone id
  1553. {"http://[::1]:", false}, // colon, but no port OK
  1554. {"http://x:", false}, // colon, but no port OK
  1555. {"http://[::1]:%38%30", true}, // not allowed: % encoding only for non-ASCII
  1556. {"http://[::1%25%41]", false}, // RFC 6874 allows over-escaping in zone
  1557. {"http://[%10::1]", true}, // no %xx escapes in IP address
  1558. {"http://[::1]/%48", false}, // %xx in path is fine
  1559. {"http://%41:8080/", true}, // not allowed: % encoding only for non-ASCII
  1560. {"mysql://x@y(z:123)/foo", true}, // not well-formed per RFC 3986, golang.org/issue/33646
  1561. {"mysql://x@y(1.2.3.4:123)/foo", true},
  1562. {" http://foo.com", true}, // invalid character in schema
  1563. {"ht tp://foo.com", true}, // invalid character in schema
  1564. {"ahttp://foo.com", false}, // valid schema characters
  1565. {"1http://foo.com", true}, // invalid character in schema
  1566. {"http://[]%20%48%54%54%50%2f%31%2e%31%0a%4d%79%48%65%61%64%65%72%3a%20%31%32%33%0a%0a/", true}, // golang.org/issue/11208
  1567. {"http://a b.com/", true}, // no space in host name please
  1568. {"cache_object://foo", true}, // scheme cannot have _, relative path cannot have : in first segment
  1569. {"cache_object:foo", true},
  1570. {"cache_object:foo/bar", true},
  1571. {"cache_object/:foo/bar", false},
  1572. }
  1573. for _, tt := range tests {
  1574. u, err := Parse(tt.in)
  1575. if tt.wantErr {
  1576. if err == nil {
  1577. t.Errorf("Parse(%q) = %#v; want an error", tt.in, u)
  1578. }
  1579. continue
  1580. }
  1581. if err != nil {
  1582. t.Errorf("Parse(%q) = %v; want no error", tt.in, err)
  1583. }
  1584. }
  1585. }
  1586. // Issue 11202
  1587. func TestStarRequest(t *testing.T) {
  1588. u, err := Parse("*")
  1589. if err != nil {
  1590. t.Fatal(err)
  1591. }
  1592. if got, want := u.RequestURI(), "*"; got != want {
  1593. t.Errorf("RequestURI = %q; want %q", got, want)
  1594. }
  1595. }
  1596. type shouldEscapeTest struct {
  1597. in byte
  1598. mode encoding
  1599. escape bool
  1600. }
  1601. var shouldEscapeTests = []shouldEscapeTest{
  1602. // Unreserved characters (§2.3)
  1603. {'a', encodePath, false},
  1604. {'a', encodeUserPassword, false},
  1605. {'a', encodeQueryComponent, false},
  1606. {'a', encodeFragment, false},
  1607. {'a', encodeHost, false},
  1608. {'z', encodePath, false},
  1609. {'A', encodePath, false},
  1610. {'Z', encodePath, false},
  1611. {'0', encodePath, false},
  1612. {'9', encodePath, false},
  1613. {'-', encodePath, false},
  1614. {'-', encodeUserPassword, false},
  1615. {'-', encodeQueryComponent, false},
  1616. {'-', encodeFragment, false},
  1617. {'.', encodePath, false},
  1618. {'_', encodePath, false},
  1619. {'~', encodePath, false},
  1620. // User information (§3.2.1)
  1621. {':', encodeUserPassword, true},
  1622. {'/', encodeUserPassword, true},
  1623. {'?', encodeUserPassword, true},
  1624. {'@', encodeUserPassword, true},
  1625. {'$', encodeUserPassword, false},
  1626. {'&', encodeUserPassword, false},
  1627. {'+', encodeUserPassword, false},
  1628. {',', encodeUserPassword, false},
  1629. {';', encodeUserPassword, false},
  1630. {'=', encodeUserPassword, false},
  1631. // Host (IP address, IPv6 address, registered name, port suffix; §3.2.2)
  1632. {'!', encodeHost, false},
  1633. {'$', encodeHost, false},
  1634. {'&', encodeHost, false},
  1635. {'\'', encodeHost, false},
  1636. {'(', encodeHost, false},
  1637. {')', encodeHost, false},
  1638. {'*', encodeHost, false},
  1639. {'+', encodeHost, false},
  1640. {',', encodeHost, false},
  1641. {';', encodeHost, false},
  1642. {'=', encodeHost, false},
  1643. {':', encodeHost, false},
  1644. {'[', encodeHost, false},
  1645. {']', encodeHost, false},
  1646. {'0', encodeHost, false},
  1647. {'9', encodeHost, false},
  1648. {'A', encodeHost, false},
  1649. {'z', encodeHost, false},
  1650. {'_', encodeHost, false},
  1651. {'-', encodeHost, false},
  1652. {'.', encodeHost, false},
  1653. }
  1654. func TestShouldEscape(t *testing.T) {
  1655. for _, tt := range shouldEscapeTests {
  1656. if shouldEscape(tt.in, tt.mode) != tt.escape {
  1657. t.Errorf("shouldEscape(%q, %v) returned %v; expected %v", tt.in, tt.mode, !tt.escape, tt.escape)
  1658. }
  1659. }
  1660. }
  1661. type timeoutError struct {
  1662. timeout bool
  1663. }
  1664. func (e *timeoutError) Error() string { return "timeout error" }
  1665. func (e *timeoutError) Timeout() bool { return e.timeout }
  1666. type temporaryError struct {
  1667. temporary bool
  1668. }
  1669. func (e *temporaryError) Error() string { return "temporary error" }
  1670. func (e *temporaryError) Temporary() bool { return e.temporary }
  1671. type timeoutTemporaryError struct {
  1672. timeoutError
  1673. temporaryError
  1674. }
  1675. func (e *timeoutTemporaryError) Error() string { return "timeout/temporary error" }
  1676. var netErrorTests = []struct {
  1677. err error
  1678. timeout bool
  1679. temporary bool
  1680. }{{
  1681. err: &Error{"Get", "http://google.com/", &timeoutError{timeout: true}},
  1682. timeout: true,
  1683. temporary: false,
  1684. }, {
  1685. err: &Error{"Get", "http://google.com/", &timeoutError{timeout: false}},
  1686. timeout: false,
  1687. temporary: false,
  1688. }, {
  1689. err: &Error{"Get", "http://google.com/", &temporaryError{temporary: true}},
  1690. timeout: false,
  1691. temporary: true,
  1692. }, {
  1693. err: &Error{"Get", "http://google.com/", &temporaryError{temporary: false}},
  1694. timeout: false,
  1695. temporary: false,
  1696. }, {
  1697. err: &Error{"Get", "http://google.com/", &timeoutTemporaryError{timeoutError{timeout: true}, temporaryError{temporary: true}}},
  1698. timeout: true,
  1699. temporary: true,
  1700. }, {
  1701. err: &Error{"Get", "http://google.com/", &timeoutTemporaryError{timeoutError{timeout: false}, temporaryError{temporary: true}}},
  1702. timeout: false,
  1703. temporary: true,
  1704. }, {
  1705. err: &Error{"Get", "http://google.com/", &timeoutTemporaryError{timeoutError{timeout: true}, temporaryError{temporary: false}}},
  1706. timeout: true,
  1707. temporary: false,
  1708. }, {
  1709. err: &Error{"Get", "http://google.com/", &timeoutTemporaryError{timeoutError{timeout: false}, temporaryError{temporary: false}}},
  1710. timeout: false,
  1711. temporary: false,
  1712. }, {
  1713. err: &Error{"Get", "http://google.com/", io.EOF},
  1714. timeout: false,
  1715. temporary: false,
  1716. }}
  1717. // Test that url.Error implements net.Error and that it forwards
  1718. func TestURLErrorImplementsNetError(t *testing.T) {
  1719. for i, tt := range netErrorTests {
  1720. err, ok := tt.err.(net.Error)
  1721. if !ok {
  1722. t.Errorf("%d: %T does not implement net.Error", i+1, tt.err)
  1723. continue
  1724. }
  1725. if err.Timeout() != tt.timeout {
  1726. t.Errorf("%d: err.Timeout(): got %v, want %v", i+1, err.Timeout(), tt.timeout)
  1727. continue
  1728. }
  1729. if err.Temporary() != tt.temporary {
  1730. t.Errorf("%d: err.Temporary(): got %v, want %v", i+1, err.Temporary(), tt.temporary)
  1731. }
  1732. }
  1733. }
  1734. func TestURLHostnameAndPort(t *testing.T) {
  1735. tests := []struct {
  1736. in string // URL.Host field
  1737. host string
  1738. port string
  1739. }{
  1740. {"foo.com:80", "foo.com", "80"},
  1741. {"foo.com", "foo.com", ""},
  1742. {"foo.com:", "foo.com", ""},
  1743. {"FOO.COM", "FOO.COM", ""}, // no canonicalization
  1744. {"1.2.3.4", "1.2.3.4", ""},
  1745. {"1.2.3.4:80", "1.2.3.4", "80"},
  1746. {"[1:2:3:4]", "1:2:3:4", ""},
  1747. {"[1:2:3:4]:80", "1:2:3:4", "80"},
  1748. {"[::1]:80", "::1", "80"},
  1749. {"[::1]", "::1", ""},
  1750. {"[::1]:", "::1", ""},
  1751. {"localhost", "localhost", ""},
  1752. {"localhost:443", "localhost", "443"},
  1753. {"some.super.long.domain.example.org:8080", "some.super.long.domain.example.org", "8080"},
  1754. {"[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:17000", "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "17000"},
  1755. {"[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", "2001:0db8:85a3:0000:0000:8a2e:0370:7334", ""},
  1756. // Ensure that even when not valid, Host is one of "Hostname",
  1757. // "Hostname:Port", "[Hostname]" or "[Hostname]:Port".
  1758. // See https://golang.org/issue/29098.
  1759. {"[google.com]:80", "google.com", "80"},
  1760. {"google.com]:80", "google.com]", "80"},
  1761. {"google.com:80_invalid_port", "google.com:80_invalid_port", ""},
  1762. {"[::1]extra]:80", "::1]extra", "80"},
  1763. {"google.com]extra:extra", "google.com]extra:extra", ""},
  1764. }
  1765. for _, tt := range tests {
  1766. u := &URL{Host: tt.in}
  1767. host, port := u.Hostname(), u.Port()
  1768. if host != tt.host {
  1769. t.Errorf("Hostname for Host %q = %q; want %q", tt.in, host, tt.host)
  1770. }
  1771. if port != tt.port {
  1772. t.Errorf("Port for Host %q = %q; want %q", tt.in, port, tt.port)
  1773. }
  1774. }
  1775. }
  1776. var _ encodingPkg.BinaryMarshaler = (*URL)(nil)
  1777. var _ encodingPkg.BinaryUnmarshaler = (*URL)(nil)
  1778. func TestJSON(t *testing.T) {
  1779. u, err := Parse("https://www.google.com/x?y=z")
  1780. if err != nil {
  1781. t.Fatal(err)
  1782. }
  1783. js, err := json.Marshal(u)
  1784. if err != nil {
  1785. t.Fatal(err)
  1786. }
  1787. // If only we could implement TextMarshaler/TextUnmarshaler,
  1788. // this would work:
  1789. //
  1790. // if string(js) != strconv.Quote(u.String()) {
  1791. // t.Errorf("json encoding: %s\nwant: %s\n", js, strconv.Quote(u.String()))
  1792. // }
  1793. u1 := new(URL)
  1794. err = json.Unmarshal(js, u1)
  1795. if err != nil {
  1796. t.Fatal(err)
  1797. }
  1798. if u1.String() != u.String() {
  1799. t.Errorf("json decoded to: %s\nwant: %s\n", u1, u)
  1800. }
  1801. }
  1802. func TestGob(t *testing.T) {
  1803. u, err := Parse("https://www.google.com/x?y=z")
  1804. if err != nil {
  1805. t.Fatal(err)
  1806. }
  1807. var w bytes.Buffer
  1808. err = gob.NewEncoder(&w).Encode(u)
  1809. if err != nil {
  1810. t.Fatal(err)
  1811. }
  1812. u1 := new(URL)
  1813. err = gob.NewDecoder(&w).Decode(u1)
  1814. if err != nil {
  1815. t.Fatal(err)
  1816. }
  1817. if u1.String() != u.String() {
  1818. t.Errorf("json decoded to: %s\nwant: %s\n", u1, u)
  1819. }
  1820. }
  1821. func TestNilUser(t *testing.T) {
  1822. defer func() {
  1823. if v := recover(); v != nil {
  1824. t.Fatalf("unexpected panic: %v", v)
  1825. }
  1826. }()
  1827. u, err := Parse("http://foo.com/")
  1828. if err != nil {
  1829. t.Fatalf("parse err: %v", err)
  1830. }
  1831. if v := u.User.Username(); v != "" {
  1832. t.Fatalf("expected empty username, got %s", v)
  1833. }
  1834. if v, ok := u.User.Password(); v != "" || ok {
  1835. t.Fatalf("expected empty password, got %s (%v)", v, ok)
  1836. }
  1837. if v := u.User.String(); v != "" {
  1838. t.Fatalf("expected empty string, got %s", v)
  1839. }
  1840. }
  1841. func TestInvalidUserPassword(t *testing.T) {
  1842. _, err := Parse("http://user^:passwo^rd@foo.com/")
  1843. if got, wantsub := fmt.Sprint(err), "net/url: invalid userinfo"; !strings.Contains(got, wantsub) {
  1844. t.Errorf("error = %q; want substring %q", got, wantsub)
  1845. }
  1846. }
  1847. func TestRejectControlCharacters(t *testing.T) {
  1848. tests := []string{
  1849. "http://foo.com/?foo\nbar",
  1850. "http\r://foo.com/",
  1851. "http://foo\x7f.com/",
  1852. }
  1853. for _, s := range tests {
  1854. _, err := Parse(s)
  1855. const wantSub = "net/url: invalid control character in URL"
  1856. if got := fmt.Sprint(err); !strings.Contains(got, wantSub) {
  1857. t.Errorf("Parse(%q) error = %q; want substring %q", s, got, wantSub)
  1858. }
  1859. }
  1860. // But don't reject non-ASCII CTLs, at least for now:
  1861. if _, err := Parse("http://foo.com/ctl\x80"); err != nil {
  1862. t.Errorf("error parsing URL with non-ASCII control byte: %v", err)
  1863. }
  1864. }
  1865. var escapeBenchmarks = []struct {
  1866. unescaped string
  1867. query string
  1868. path string
  1869. }{
  1870. {
  1871. unescaped: "one two",
  1872. query: "one+two",
  1873. path: "one%20two",
  1874. },
  1875. {
  1876. unescaped: "Фотки собак",
  1877. query: "%D0%A4%D0%BE%D1%82%D0%BA%D0%B8+%D1%81%D0%BE%D0%B1%D0%B0%D0%BA",
  1878. path: "%D0%A4%D0%BE%D1%82%D0%BA%D0%B8%20%D1%81%D0%BE%D0%B1%D0%B0%D0%BA",
  1879. },
  1880. {
  1881. unescaped: "shortrun(break)shortrun",
  1882. query: "shortrun%28break%29shortrun",
  1883. path: "shortrun%28break%29shortrun",
  1884. },
  1885. {
  1886. unescaped: "longerrunofcharacters(break)anotherlongerrunofcharacters",
  1887. query: "longerrunofcharacters%28break%29anotherlongerrunofcharacters",
  1888. path: "longerrunofcharacters%28break%29anotherlongerrunofcharacters",
  1889. },
  1890. {
  1891. unescaped: strings.Repeat("padded/with+various%characters?that=need$some@escaping+paddedsowebreak/256bytes", 4),
  1892. query: strings.Repeat("padded%2Fwith%2Bvarious%25characters%3Fthat%3Dneed%24some%40escaping%2Bpaddedsowebreak%2F256bytes", 4),
  1893. path: strings.Repeat("padded%2Fwith+various%25characters%3Fthat=need$some@escaping+paddedsowebreak%2F256bytes", 4),
  1894. },
  1895. }
  1896. func BenchmarkQueryEscape(b *testing.B) {
  1897. for _, tc := range escapeBenchmarks {
  1898. b.Run("", func(b *testing.B) {
  1899. b.ReportAllocs()
  1900. var g string
  1901. for i := 0; i < b.N; i++ {
  1902. g = QueryEscape(tc.unescaped)
  1903. }
  1904. b.StopTimer()
  1905. if g != tc.query {
  1906. b.Errorf("QueryEscape(%q) == %q, want %q", tc.unescaped, g, tc.query)
  1907. }
  1908. })
  1909. }
  1910. }
  1911. func BenchmarkPathEscape(b *testing.B) {
  1912. for _, tc := range escapeBenchmarks {
  1913. b.Run("", func(b *testing.B) {
  1914. b.ReportAllocs()
  1915. var g string
  1916. for i := 0; i < b.N; i++ {
  1917. g = PathEscape(tc.unescaped)
  1918. }
  1919. b.StopTimer()
  1920. if g != tc.path {
  1921. b.Errorf("PathEscape(%q) == %q, want %q", tc.unescaped, g, tc.path)
  1922. }
  1923. })
  1924. }
  1925. }
  1926. func BenchmarkQueryUnescape(b *testing.B) {
  1927. for _, tc := range escapeBenchmarks {
  1928. b.Run("", func(b *testing.B) {
  1929. b.ReportAllocs()
  1930. var g string
  1931. for i := 0; i < b.N; i++ {
  1932. g, _ = QueryUnescape(tc.query)
  1933. }
  1934. b.StopTimer()
  1935. if g != tc.unescaped {
  1936. b.Errorf("QueryUnescape(%q) == %q, want %q", tc.query, g, tc.unescaped)
  1937. }
  1938. })
  1939. }
  1940. }
  1941. func BenchmarkPathUnescape(b *testing.B) {
  1942. for _, tc := range escapeBenchmarks {
  1943. b.Run("", func(b *testing.B) {
  1944. b.ReportAllocs()
  1945. var g string
  1946. for i := 0; i < b.N; i++ {
  1947. g, _ = PathUnescape(tc.path)
  1948. }
  1949. b.StopTimer()
  1950. if g != tc.unescaped {
  1951. b.Errorf("PathUnescape(%q) == %q, want %q", tc.path, g, tc.unescaped)
  1952. }
  1953. })
  1954. }
  1955. }