api-select.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. /*
  2. * MinIO Go Library for Amazon S3 Compatible Cloud Storage
  3. * (C) 2018-2020 MinIO, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package minio
  18. import (
  19. "bytes"
  20. "context"
  21. "encoding/binary"
  22. "encoding/xml"
  23. "errors"
  24. "fmt"
  25. "hash"
  26. "hash/crc32"
  27. "io"
  28. "net/http"
  29. "net/url"
  30. "strings"
  31. "github.com/minio/minio-go/v7/pkg/encrypt"
  32. "github.com/minio/minio-go/v7/pkg/s3utils"
  33. )
  34. // CSVFileHeaderInfo - is the parameter for whether to utilize headers.
  35. type CSVFileHeaderInfo string
  36. // Constants for file header info.
  37. const (
  38. CSVFileHeaderInfoNone CSVFileHeaderInfo = "NONE"
  39. CSVFileHeaderInfoIgnore = "IGNORE"
  40. CSVFileHeaderInfoUse = "USE"
  41. )
  42. // SelectCompressionType - is the parameter for what type of compression is
  43. // present
  44. type SelectCompressionType string
  45. // Constants for compression types under select API.
  46. const (
  47. SelectCompressionNONE SelectCompressionType = "NONE"
  48. SelectCompressionGZIP = "GZIP"
  49. SelectCompressionBZIP = "BZIP2"
  50. // Non-standard compression schemes, supported by MinIO hosts:
  51. SelectCompressionZSTD = "ZSTD" // Zstandard compression.
  52. SelectCompressionLZ4 = "LZ4" // LZ4 Stream
  53. SelectCompressionS2 = "S2" // S2 Stream
  54. SelectCompressionSNAPPY = "SNAPPY" // Snappy stream
  55. )
  56. // CSVQuoteFields - is the parameter for how CSV fields are quoted.
  57. type CSVQuoteFields string
  58. // Constants for csv quote styles.
  59. const (
  60. CSVQuoteFieldsAlways CSVQuoteFields = "Always"
  61. CSVQuoteFieldsAsNeeded = "AsNeeded"
  62. )
  63. // QueryExpressionType - is of what syntax the expression is, this should only
  64. // be SQL
  65. type QueryExpressionType string
  66. // Constants for expression type.
  67. const (
  68. QueryExpressionTypeSQL QueryExpressionType = "SQL"
  69. )
  70. // JSONType determines json input serialization type.
  71. type JSONType string
  72. // Constants for JSONTypes.
  73. const (
  74. JSONDocumentType JSONType = "DOCUMENT"
  75. JSONLinesType = "LINES"
  76. )
  77. // ParquetInputOptions parquet input specific options
  78. type ParquetInputOptions struct{}
  79. // CSVInputOptions csv input specific options
  80. type CSVInputOptions struct {
  81. FileHeaderInfo CSVFileHeaderInfo
  82. fileHeaderInfoSet bool
  83. RecordDelimiter string
  84. recordDelimiterSet bool
  85. FieldDelimiter string
  86. fieldDelimiterSet bool
  87. QuoteCharacter string
  88. quoteCharacterSet bool
  89. QuoteEscapeCharacter string
  90. quoteEscapeCharacterSet bool
  91. Comments string
  92. commentsSet bool
  93. }
  94. // SetFileHeaderInfo sets the file header info in the CSV input options
  95. func (c *CSVInputOptions) SetFileHeaderInfo(val CSVFileHeaderInfo) {
  96. c.FileHeaderInfo = val
  97. c.fileHeaderInfoSet = true
  98. }
  99. // SetRecordDelimiter sets the record delimiter in the CSV input options
  100. func (c *CSVInputOptions) SetRecordDelimiter(val string) {
  101. c.RecordDelimiter = val
  102. c.recordDelimiterSet = true
  103. }
  104. // SetFieldDelimiter sets the field delimiter in the CSV input options
  105. func (c *CSVInputOptions) SetFieldDelimiter(val string) {
  106. c.FieldDelimiter = val
  107. c.fieldDelimiterSet = true
  108. }
  109. // SetQuoteCharacter sets the quote character in the CSV input options
  110. func (c *CSVInputOptions) SetQuoteCharacter(val string) {
  111. c.QuoteCharacter = val
  112. c.quoteCharacterSet = true
  113. }
  114. // SetQuoteEscapeCharacter sets the quote escape character in the CSV input options
  115. func (c *CSVInputOptions) SetQuoteEscapeCharacter(val string) {
  116. c.QuoteEscapeCharacter = val
  117. c.quoteEscapeCharacterSet = true
  118. }
  119. // SetComments sets the comments character in the CSV input options
  120. func (c *CSVInputOptions) SetComments(val string) {
  121. c.Comments = val
  122. c.commentsSet = true
  123. }
  124. // MarshalXML - produces the xml representation of the CSV input options struct
  125. func (c CSVInputOptions) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
  126. if err := e.EncodeToken(start); err != nil {
  127. return err
  128. }
  129. if c.FileHeaderInfo != "" || c.fileHeaderInfoSet {
  130. if err := e.EncodeElement(c.FileHeaderInfo, xml.StartElement{Name: xml.Name{Local: "FileHeaderInfo"}}); err != nil {
  131. return err
  132. }
  133. }
  134. if c.RecordDelimiter != "" || c.recordDelimiterSet {
  135. if err := e.EncodeElement(c.RecordDelimiter, xml.StartElement{Name: xml.Name{Local: "RecordDelimiter"}}); err != nil {
  136. return err
  137. }
  138. }
  139. if c.FieldDelimiter != "" || c.fieldDelimiterSet {
  140. if err := e.EncodeElement(c.FieldDelimiter, xml.StartElement{Name: xml.Name{Local: "FieldDelimiter"}}); err != nil {
  141. return err
  142. }
  143. }
  144. if c.QuoteCharacter != "" || c.quoteCharacterSet {
  145. if err := e.EncodeElement(c.QuoteCharacter, xml.StartElement{Name: xml.Name{Local: "QuoteCharacter"}}); err != nil {
  146. return err
  147. }
  148. }
  149. if c.QuoteEscapeCharacter != "" || c.quoteEscapeCharacterSet {
  150. if err := e.EncodeElement(c.QuoteEscapeCharacter, xml.StartElement{Name: xml.Name{Local: "QuoteEscapeCharacter"}}); err != nil {
  151. return err
  152. }
  153. }
  154. if c.Comments != "" || c.commentsSet {
  155. if err := e.EncodeElement(c.Comments, xml.StartElement{Name: xml.Name{Local: "Comments"}}); err != nil {
  156. return err
  157. }
  158. }
  159. return e.EncodeToken(xml.EndElement{Name: start.Name})
  160. }
  161. // CSVOutputOptions csv output specific options
  162. type CSVOutputOptions struct {
  163. QuoteFields CSVQuoteFields
  164. quoteFieldsSet bool
  165. RecordDelimiter string
  166. recordDelimiterSet bool
  167. FieldDelimiter string
  168. fieldDelimiterSet bool
  169. QuoteCharacter string
  170. quoteCharacterSet bool
  171. QuoteEscapeCharacter string
  172. quoteEscapeCharacterSet bool
  173. }
  174. // SetQuoteFields sets the quote field parameter in the CSV output options
  175. func (c *CSVOutputOptions) SetQuoteFields(val CSVQuoteFields) {
  176. c.QuoteFields = val
  177. c.quoteFieldsSet = true
  178. }
  179. // SetRecordDelimiter sets the record delimiter character in the CSV output options
  180. func (c *CSVOutputOptions) SetRecordDelimiter(val string) {
  181. c.RecordDelimiter = val
  182. c.recordDelimiterSet = true
  183. }
  184. // SetFieldDelimiter sets the field delimiter character in the CSV output options
  185. func (c *CSVOutputOptions) SetFieldDelimiter(val string) {
  186. c.FieldDelimiter = val
  187. c.fieldDelimiterSet = true
  188. }
  189. // SetQuoteCharacter sets the quote character in the CSV output options
  190. func (c *CSVOutputOptions) SetQuoteCharacter(val string) {
  191. c.QuoteCharacter = val
  192. c.quoteCharacterSet = true
  193. }
  194. // SetQuoteEscapeCharacter sets the quote escape character in the CSV output options
  195. func (c *CSVOutputOptions) SetQuoteEscapeCharacter(val string) {
  196. c.QuoteEscapeCharacter = val
  197. c.quoteEscapeCharacterSet = true
  198. }
  199. // MarshalXML - produces the xml representation of the CSVOutputOptions struct
  200. func (c CSVOutputOptions) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
  201. if err := e.EncodeToken(start); err != nil {
  202. return err
  203. }
  204. if c.QuoteFields != "" || c.quoteFieldsSet {
  205. if err := e.EncodeElement(c.QuoteFields, xml.StartElement{Name: xml.Name{Local: "QuoteFields"}}); err != nil {
  206. return err
  207. }
  208. }
  209. if c.RecordDelimiter != "" || c.recordDelimiterSet {
  210. if err := e.EncodeElement(c.RecordDelimiter, xml.StartElement{Name: xml.Name{Local: "RecordDelimiter"}}); err != nil {
  211. return err
  212. }
  213. }
  214. if c.FieldDelimiter != "" || c.fieldDelimiterSet {
  215. if err := e.EncodeElement(c.FieldDelimiter, xml.StartElement{Name: xml.Name{Local: "FieldDelimiter"}}); err != nil {
  216. return err
  217. }
  218. }
  219. if c.QuoteCharacter != "" || c.quoteCharacterSet {
  220. if err := e.EncodeElement(c.QuoteCharacter, xml.StartElement{Name: xml.Name{Local: "QuoteCharacter"}}); err != nil {
  221. return err
  222. }
  223. }
  224. if c.QuoteEscapeCharacter != "" || c.quoteEscapeCharacterSet {
  225. if err := e.EncodeElement(c.QuoteEscapeCharacter, xml.StartElement{Name: xml.Name{Local: "QuoteEscapeCharacter"}}); err != nil {
  226. return err
  227. }
  228. }
  229. return e.EncodeToken(xml.EndElement{Name: start.Name})
  230. }
  231. // JSONInputOptions json input specific options
  232. type JSONInputOptions struct {
  233. Type JSONType
  234. typeSet bool
  235. }
  236. // SetType sets the JSON type in the JSON input options
  237. func (j *JSONInputOptions) SetType(typ JSONType) {
  238. j.Type = typ
  239. j.typeSet = true
  240. }
  241. // MarshalXML - produces the xml representation of the JSONInputOptions struct
  242. func (j JSONInputOptions) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
  243. if err := e.EncodeToken(start); err != nil {
  244. return err
  245. }
  246. if j.Type != "" || j.typeSet {
  247. if err := e.EncodeElement(j.Type, xml.StartElement{Name: xml.Name{Local: "Type"}}); err != nil {
  248. return err
  249. }
  250. }
  251. return e.EncodeToken(xml.EndElement{Name: start.Name})
  252. }
  253. // JSONOutputOptions - json output specific options
  254. type JSONOutputOptions struct {
  255. RecordDelimiter string
  256. recordDelimiterSet bool
  257. }
  258. // SetRecordDelimiter sets the record delimiter in the JSON output options
  259. func (j *JSONOutputOptions) SetRecordDelimiter(val string) {
  260. j.RecordDelimiter = val
  261. j.recordDelimiterSet = true
  262. }
  263. // MarshalXML - produces the xml representation of the JSONOutputOptions struct
  264. func (j JSONOutputOptions) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
  265. if err := e.EncodeToken(start); err != nil {
  266. return err
  267. }
  268. if j.RecordDelimiter != "" || j.recordDelimiterSet {
  269. if err := e.EncodeElement(j.RecordDelimiter, xml.StartElement{Name: xml.Name{Local: "RecordDelimiter"}}); err != nil {
  270. return err
  271. }
  272. }
  273. return e.EncodeToken(xml.EndElement{Name: start.Name})
  274. }
  275. // SelectObjectInputSerialization - input serialization parameters
  276. type SelectObjectInputSerialization struct {
  277. CompressionType SelectCompressionType `xml:"CompressionType,omitempty"`
  278. Parquet *ParquetInputOptions `xml:"Parquet,omitempty"`
  279. CSV *CSVInputOptions `xml:"CSV,omitempty"`
  280. JSON *JSONInputOptions `xml:"JSON,omitempty"`
  281. }
  282. // SelectObjectOutputSerialization - output serialization parameters.
  283. type SelectObjectOutputSerialization struct {
  284. CSV *CSVOutputOptions `xml:"CSV,omitempty"`
  285. JSON *JSONOutputOptions `xml:"JSON,omitempty"`
  286. }
  287. // SelectObjectOptions - represents the input select body
  288. type SelectObjectOptions struct {
  289. XMLName xml.Name `xml:"SelectObjectContentRequest" json:"-"`
  290. ServerSideEncryption encrypt.ServerSide `xml:"-"`
  291. Expression string
  292. ExpressionType QueryExpressionType
  293. InputSerialization SelectObjectInputSerialization
  294. OutputSerialization SelectObjectOutputSerialization
  295. RequestProgress struct {
  296. Enabled bool
  297. }
  298. }
  299. // Header returns the http.Header representation of the SelectObject options.
  300. func (o SelectObjectOptions) Header() http.Header {
  301. headers := make(http.Header)
  302. if o.ServerSideEncryption != nil && o.ServerSideEncryption.Type() == encrypt.SSEC {
  303. o.ServerSideEncryption.Marshal(headers)
  304. }
  305. return headers
  306. }
  307. // SelectObjectType - is the parameter which defines what type of object the
  308. // operation is being performed on.
  309. type SelectObjectType string
  310. // Constants for input data types.
  311. const (
  312. SelectObjectTypeCSV SelectObjectType = "CSV"
  313. SelectObjectTypeJSON = "JSON"
  314. SelectObjectTypeParquet = "Parquet"
  315. )
  316. // preludeInfo is used for keeping track of necessary information from the
  317. // prelude.
  318. type preludeInfo struct {
  319. totalLen uint32
  320. headerLen uint32
  321. }
  322. // SelectResults is used for the streaming responses from the server.
  323. type SelectResults struct {
  324. pipeReader *io.PipeReader
  325. resp *http.Response
  326. stats *StatsMessage
  327. progress *ProgressMessage
  328. }
  329. // ProgressMessage is a struct for progress xml message.
  330. type ProgressMessage struct {
  331. XMLName xml.Name `xml:"Progress" json:"-"`
  332. StatsMessage
  333. }
  334. // StatsMessage is a struct for stat xml message.
  335. type StatsMessage struct {
  336. XMLName xml.Name `xml:"Stats" json:"-"`
  337. BytesScanned int64
  338. BytesProcessed int64
  339. BytesReturned int64
  340. }
  341. // messageType represents the type of message.
  342. type messageType string
  343. const (
  344. errorMsg messageType = "error"
  345. commonMsg = "event"
  346. )
  347. // eventType represents the type of event.
  348. type eventType string
  349. // list of event-types returned by Select API.
  350. const (
  351. endEvent eventType = "End"
  352. recordsEvent = "Records"
  353. progressEvent = "Progress"
  354. statsEvent = "Stats"
  355. )
  356. // contentType represents content type of event.
  357. type contentType string
  358. const (
  359. xmlContent contentType = "text/xml"
  360. )
  361. // SelectObjectContent is a implementation of http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectSELECTContent.html AWS S3 API.
  362. func (c Client) SelectObjectContent(ctx context.Context, bucketName, objectName string, opts SelectObjectOptions) (*SelectResults, error) {
  363. // Input validation.
  364. if err := s3utils.CheckValidBucketName(bucketName); err != nil {
  365. return nil, err
  366. }
  367. if err := s3utils.CheckValidObjectName(objectName); err != nil {
  368. return nil, err
  369. }
  370. selectReqBytes, err := xml.Marshal(opts)
  371. if err != nil {
  372. return nil, err
  373. }
  374. urlValues := make(url.Values)
  375. urlValues.Set("select", "")
  376. urlValues.Set("select-type", "2")
  377. // Execute POST on bucket/object.
  378. resp, err := c.executeMethod(ctx, http.MethodPost, requestMetadata{
  379. bucketName: bucketName,
  380. objectName: objectName,
  381. queryValues: urlValues,
  382. customHeader: opts.Header(),
  383. contentMD5Base64: sumMD5Base64(selectReqBytes),
  384. contentSHA256Hex: sum256Hex(selectReqBytes),
  385. contentBody: bytes.NewReader(selectReqBytes),
  386. contentLength: int64(len(selectReqBytes)),
  387. })
  388. if err != nil {
  389. return nil, err
  390. }
  391. return NewSelectResults(resp, bucketName)
  392. }
  393. // NewSelectResults creates a Select Result parser that parses the response
  394. // and returns a Reader that will return parsed and assembled select output.
  395. func NewSelectResults(resp *http.Response, bucketName string) (*SelectResults, error) {
  396. if resp.StatusCode != http.StatusOK {
  397. return nil, httpRespToErrorResponse(resp, bucketName, "")
  398. }
  399. pipeReader, pipeWriter := io.Pipe()
  400. streamer := &SelectResults{
  401. resp: resp,
  402. stats: &StatsMessage{},
  403. progress: &ProgressMessage{},
  404. pipeReader: pipeReader,
  405. }
  406. streamer.start(pipeWriter)
  407. return streamer, nil
  408. }
  409. // Close - closes the underlying response body and the stream reader.
  410. func (s *SelectResults) Close() error {
  411. defer closeResponse(s.resp)
  412. return s.pipeReader.Close()
  413. }
  414. // Read - is a reader compatible implementation for SelectObjectContent records.
  415. func (s *SelectResults) Read(b []byte) (n int, err error) {
  416. return s.pipeReader.Read(b)
  417. }
  418. // Stats - information about a request's stats when processing is complete.
  419. func (s *SelectResults) Stats() *StatsMessage {
  420. return s.stats
  421. }
  422. // Progress - information about the progress of a request.
  423. func (s *SelectResults) Progress() *ProgressMessage {
  424. return s.progress
  425. }
  426. // start is the main function that decodes the large byte array into
  427. // several events that are sent through the eventstream.
  428. func (s *SelectResults) start(pipeWriter *io.PipeWriter) {
  429. go func() {
  430. for {
  431. var prelude preludeInfo
  432. var headers = make(http.Header)
  433. var err error
  434. // Create CRC code
  435. crc := crc32.New(crc32.IEEETable)
  436. crcReader := io.TeeReader(s.resp.Body, crc)
  437. // Extract the prelude(12 bytes) into a struct to extract relevant information.
  438. prelude, err = processPrelude(crcReader, crc)
  439. if err != nil {
  440. pipeWriter.CloseWithError(err)
  441. closeResponse(s.resp)
  442. return
  443. }
  444. // Extract the headers(variable bytes) into a struct to extract relevant information
  445. if prelude.headerLen > 0 {
  446. if err = extractHeader(io.LimitReader(crcReader, int64(prelude.headerLen)), headers); err != nil {
  447. pipeWriter.CloseWithError(err)
  448. closeResponse(s.resp)
  449. return
  450. }
  451. }
  452. // Get the actual payload length so that the appropriate amount of
  453. // bytes can be read or parsed.
  454. payloadLen := prelude.PayloadLen()
  455. m := messageType(headers.Get("message-type"))
  456. switch m {
  457. case errorMsg:
  458. pipeWriter.CloseWithError(errors.New(headers.Get("error-code") + ":\"" + headers.Get("error-message") + "\""))
  459. closeResponse(s.resp)
  460. return
  461. case commonMsg:
  462. // Get content-type of the payload.
  463. c := contentType(headers.Get("content-type"))
  464. // Get event type of the payload.
  465. e := eventType(headers.Get("event-type"))
  466. // Handle all supported events.
  467. switch e {
  468. case endEvent:
  469. pipeWriter.Close()
  470. closeResponse(s.resp)
  471. return
  472. case recordsEvent:
  473. if _, err = io.Copy(pipeWriter, io.LimitReader(crcReader, payloadLen)); err != nil {
  474. pipeWriter.CloseWithError(err)
  475. closeResponse(s.resp)
  476. return
  477. }
  478. case progressEvent:
  479. switch c {
  480. case xmlContent:
  481. if err = xmlDecoder(io.LimitReader(crcReader, payloadLen), s.progress); err != nil {
  482. pipeWriter.CloseWithError(err)
  483. closeResponse(s.resp)
  484. return
  485. }
  486. default:
  487. pipeWriter.CloseWithError(fmt.Errorf("Unexpected content-type %s sent for event-type %s", c, progressEvent))
  488. closeResponse(s.resp)
  489. return
  490. }
  491. case statsEvent:
  492. switch c {
  493. case xmlContent:
  494. if err = xmlDecoder(io.LimitReader(crcReader, payloadLen), s.stats); err != nil {
  495. pipeWriter.CloseWithError(err)
  496. closeResponse(s.resp)
  497. return
  498. }
  499. default:
  500. pipeWriter.CloseWithError(fmt.Errorf("Unexpected content-type %s sent for event-type %s", c, statsEvent))
  501. closeResponse(s.resp)
  502. return
  503. }
  504. }
  505. }
  506. // Ensures that the full message's CRC is correct and
  507. // that the message is not corrupted
  508. if err := checkCRC(s.resp.Body, crc.Sum32()); err != nil {
  509. pipeWriter.CloseWithError(err)
  510. closeResponse(s.resp)
  511. return
  512. }
  513. }
  514. }()
  515. }
  516. // PayloadLen is a function that calculates the length of the payload.
  517. func (p preludeInfo) PayloadLen() int64 {
  518. return int64(p.totalLen - p.headerLen - 16)
  519. }
  520. // processPrelude is the function that reads the 12 bytes of the prelude and
  521. // ensures the CRC is correct while also extracting relevant information into
  522. // the struct,
  523. func processPrelude(prelude io.Reader, crc hash.Hash32) (preludeInfo, error) {
  524. var err error
  525. var pInfo = preludeInfo{}
  526. // reads total length of the message (first 4 bytes)
  527. pInfo.totalLen, err = extractUint32(prelude)
  528. if err != nil {
  529. return pInfo, err
  530. }
  531. // reads total header length of the message (2nd 4 bytes)
  532. pInfo.headerLen, err = extractUint32(prelude)
  533. if err != nil {
  534. return pInfo, err
  535. }
  536. // checks that the CRC is correct (3rd 4 bytes)
  537. preCRC := crc.Sum32()
  538. if err := checkCRC(prelude, preCRC); err != nil {
  539. return pInfo, err
  540. }
  541. return pInfo, nil
  542. }
  543. // extracts the relevant information from the Headers.
  544. func extractHeader(body io.Reader, myHeaders http.Header) error {
  545. for {
  546. // extracts the first part of the header,
  547. headerTypeName, err := extractHeaderType(body)
  548. if err != nil {
  549. // Since end of file, we have read all of our headers
  550. if err == io.EOF {
  551. break
  552. }
  553. return err
  554. }
  555. // reads the 7 present in the header and ignores it.
  556. extractUint8(body)
  557. headerValueName, err := extractHeaderValue(body)
  558. if err != nil {
  559. return err
  560. }
  561. myHeaders.Set(headerTypeName, headerValueName)
  562. }
  563. return nil
  564. }
  565. // extractHeaderType extracts the first half of the header message, the header type.
  566. func extractHeaderType(body io.Reader) (string, error) {
  567. // extracts 2 bit integer
  568. headerNameLen, err := extractUint8(body)
  569. if err != nil {
  570. return "", err
  571. }
  572. // extracts the string with the appropriate number of bytes
  573. headerName, err := extractString(body, int(headerNameLen))
  574. if err != nil {
  575. return "", err
  576. }
  577. return strings.TrimPrefix(headerName, ":"), nil
  578. }
  579. // extractsHeaderValue extracts the second half of the header message, the
  580. // header value
  581. func extractHeaderValue(body io.Reader) (string, error) {
  582. bodyLen, err := extractUint16(body)
  583. if err != nil {
  584. return "", err
  585. }
  586. bodyName, err := extractString(body, int(bodyLen))
  587. if err != nil {
  588. return "", err
  589. }
  590. return bodyName, nil
  591. }
  592. // extracts a string from byte array of a particular number of bytes.
  593. func extractString(source io.Reader, lenBytes int) (string, error) {
  594. myVal := make([]byte, lenBytes)
  595. _, err := source.Read(myVal)
  596. if err != nil {
  597. return "", err
  598. }
  599. return string(myVal), nil
  600. }
  601. // extractUint32 extracts a 4 byte integer from the byte array.
  602. func extractUint32(r io.Reader) (uint32, error) {
  603. buf := make([]byte, 4)
  604. _, err := readFull(r, buf)
  605. if err != nil {
  606. return 0, err
  607. }
  608. return binary.BigEndian.Uint32(buf), nil
  609. }
  610. // extractUint16 extracts a 2 byte integer from the byte array.
  611. func extractUint16(r io.Reader) (uint16, error) {
  612. buf := make([]byte, 2)
  613. _, err := readFull(r, buf)
  614. if err != nil {
  615. return 0, err
  616. }
  617. return binary.BigEndian.Uint16(buf), nil
  618. }
  619. // extractUint8 extracts a 1 byte integer from the byte array.
  620. func extractUint8(r io.Reader) (uint8, error) {
  621. buf := make([]byte, 1)
  622. _, err := readFull(r, buf)
  623. if err != nil {
  624. return 0, err
  625. }
  626. return buf[0], nil
  627. }
  628. // checkCRC ensures that the CRC matches with the one from the reader.
  629. func checkCRC(r io.Reader, expect uint32) error {
  630. msgCRC, err := extractUint32(r)
  631. if err != nil {
  632. return err
  633. }
  634. if msgCRC != expect {
  635. return fmt.Errorf("Checksum Mismatch, MessageCRC of 0x%X does not equal expected CRC of 0x%X", msgCRC, expect)
  636. }
  637. return nil
  638. }