1// Copyright (C) MongoDB, Inc. 2017-present. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); you may 4// not use this file except in compliance with the License. You may obtain 5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 7package bsoncore 8 9import ( 10 "bytes" 11 "errors" 12 "fmt" 13 "io" 14 "strconv" 15 16 "github.com/go-stack/stack" 17 "go.mongodb.org/mongo-driver/bson/bsontype" 18) 19 20// ValidationError is an error type returned when attempting to validate a document or array. 21type ValidationError string 22 23func (ve ValidationError) Error() string { return string(ve) } 24 25// NewDocumentLengthError creates and returns an error for when the length of a document exceeds the 26// bytes available. 27func NewDocumentLengthError(length, rem int) error { 28 return lengthError("document", length, rem) 29} 30 31func lengthError(bufferType string, length, rem int) error { 32 return ValidationError(fmt.Sprintf("%v length exceeds available bytes. length=%d remainingBytes=%d", 33 bufferType, length, rem)) 34} 35 36// InsufficientBytesError indicates that there were not enough bytes to read the next component. 37type InsufficientBytesError struct { 38 Source []byte 39 Remaining []byte 40 Stack stack.CallStack 41} 42 43// NewInsufficientBytesError creates a new InsufficientBytesError with the given Document, remaining 44// bytes, and the current stack. 45func NewInsufficientBytesError(src, rem []byte) InsufficientBytesError { 46 return InsufficientBytesError{Source: src, Remaining: rem, Stack: stack.Trace().TrimRuntime()} 47} 48 49// Error implements the error interface. 50func (ibe InsufficientBytesError) Error() string { 51 return "too few bytes to read next component" 52} 53 54// ErrorStack returns a string representing the stack at the point where the error occurred. 55func (ibe InsufficientBytesError) ErrorStack() string { 56 s := bytes.NewBufferString("too few bytes to read next component: [") 57 58 for i, call := range ibe.Stack { 59 if i != 0 { 60 s.WriteString(", ") 61 } 62 63 // go vet doesn't like %k even though it's part of stack's API, so we move the format 64 // string so it doesn't complain. (We also can't make it a constant, or go vet still 65 // complains.) 66 callFormat := "%k.%n %v" 67 68 s.WriteString(fmt.Sprintf(callFormat, call, call, call)) 69 } 70 71 s.WriteRune(']') 72 73 return s.String() 74} 75 76// Equal checks that err2 also is an ErrTooSmall. 77func (ibe InsufficientBytesError) Equal(err2 error) bool { 78 switch err2.(type) { 79 case InsufficientBytesError: 80 return true 81 default: 82 return false 83 } 84} 85 86// InvalidDepthTraversalError is returned when attempting a recursive Lookup when one component of 87// the path is neither an embedded document nor an array. 88type InvalidDepthTraversalError struct { 89 Key string 90 Type bsontype.Type 91} 92 93func (idte InvalidDepthTraversalError) Error() string { 94 return fmt.Sprintf( 95 "attempt to traverse into %s, but it's type is %s, not %s nor %s", 96 idte.Key, idte.Type, bsontype.EmbeddedDocument, bsontype.Array, 97 ) 98} 99 100// ErrMissingNull is returned when a document or array's last byte is not null. 101const ErrMissingNull ValidationError = "document or array end is missing null byte" 102 103// ErrInvalidLength indicates that a length in a binary representation of a BSON document or array 104// is invalid. 105const ErrInvalidLength ValidationError = "document or array length is invalid" 106 107// ErrNilReader indicates that an operation was attempted on a nil io.Reader. 108var ErrNilReader = errors.New("nil reader") 109 110// ErrEmptyKey indicates that no key was provided to a Lookup method. 111var ErrEmptyKey = errors.New("empty key provided") 112 113// ErrElementNotFound indicates that an Element matching a certain condition does not exist. 114var ErrElementNotFound = errors.New("element not found") 115 116// ErrOutOfBounds indicates that an index provided to access something was invalid. 117var ErrOutOfBounds = errors.New("out of bounds") 118 119// Document is a raw bytes representation of a BSON document. 120type Document []byte 121 122// NewDocumentFromReader reads a document from r. This function will only validate the length is 123// correct and that the document ends with a null byte. 124func NewDocumentFromReader(r io.Reader) (Document, error) { 125 return newBufferFromReader(r) 126} 127 128func newBufferFromReader(r io.Reader) ([]byte, error) { 129 if r == nil { 130 return nil, ErrNilReader 131 } 132 133 var lengthBytes [4]byte 134 135 // ReadFull guarantees that we will have read at least len(lengthBytes) if err == nil 136 _, err := io.ReadFull(r, lengthBytes[:]) 137 if err != nil { 138 return nil, err 139 } 140 141 length, _, _ := readi32(lengthBytes[:]) // ignore ok since we always have enough bytes to read a length 142 if length < 0 { 143 return nil, ErrInvalidLength 144 } 145 buffer := make([]byte, length) 146 147 copy(buffer, lengthBytes[:]) 148 149 _, err = io.ReadFull(r, buffer[4:]) 150 if err != nil { 151 return nil, err 152 } 153 154 if buffer[length-1] != 0x00 { 155 return nil, ErrMissingNull 156 } 157 158 return buffer, nil 159} 160 161// Lookup searches the document, potentially recursively, for the given key. If there are multiple 162// keys provided, this method will recurse down, as long as the top and intermediate nodes are 163// either documents or arrays. If an error occurs or if the value doesn't exist, an empty Value is 164// returned. 165func (d Document) Lookup(key ...string) Value { 166 val, _ := d.LookupErr(key...) 167 return val 168} 169 170// LookupErr is the same as Lookup, except it returns an error in addition to an empty Value. 171func (d Document) LookupErr(key ...string) (Value, error) { 172 if len(key) < 1 { 173 return Value{}, ErrEmptyKey 174 } 175 length, rem, ok := ReadLength(d) 176 if !ok { 177 return Value{}, NewInsufficientBytesError(d, rem) 178 } 179 180 length -= 4 181 182 var elem Element 183 for length > 1 { 184 elem, rem, ok = ReadElement(rem) 185 length -= int32(len(elem)) 186 if !ok { 187 return Value{}, NewInsufficientBytesError(d, rem) 188 } 189 // We use `KeyBytes` rather than `Key` to avoid a needless string alloc. 190 if string(elem.KeyBytes()) != key[0] { 191 continue 192 } 193 if len(key) > 1 { 194 tt := bsontype.Type(elem[0]) 195 switch tt { 196 case bsontype.EmbeddedDocument: 197 val, err := elem.Value().Document().LookupErr(key[1:]...) 198 if err != nil { 199 return Value{}, err 200 } 201 return val, nil 202 case bsontype.Array: 203 val, err := elem.Value().Array().LookupErr(key[1:]...) 204 if err != nil { 205 return Value{}, err 206 } 207 return val, nil 208 default: 209 return Value{}, InvalidDepthTraversalError{Key: elem.Key(), Type: tt} 210 } 211 } 212 return elem.ValueErr() 213 } 214 return Value{}, ErrElementNotFound 215} 216 217// Index searches for and retrieves the element at the given index. This method will panic if 218// the document is invalid or if the index is out of bounds. 219func (d Document) Index(index uint) Element { 220 elem, err := d.IndexErr(index) 221 if err != nil { 222 panic(err) 223 } 224 return elem 225} 226 227// IndexErr searches for and retrieves the element at the given index. 228func (d Document) IndexErr(index uint) (Element, error) { 229 return indexErr(d, index) 230} 231 232func indexErr(b []byte, index uint) (Element, error) { 233 length, rem, ok := ReadLength(b) 234 if !ok { 235 return nil, NewInsufficientBytesError(b, rem) 236 } 237 238 length -= 4 239 240 var current uint 241 var elem Element 242 for length > 1 { 243 elem, rem, ok = ReadElement(rem) 244 length -= int32(len(elem)) 245 if !ok { 246 return nil, NewInsufficientBytesError(b, rem) 247 } 248 if current != index { 249 current++ 250 continue 251 } 252 return elem, nil 253 } 254 return nil, ErrOutOfBounds 255} 256 257// DebugString outputs a human readable version of Document. It will attempt to stringify the 258// valid components of the document even if the entire document is not valid. 259func (d Document) DebugString() string { 260 if len(d) < 5 { 261 return "<malformed>" 262 } 263 var buf bytes.Buffer 264 buf.WriteString("Document") 265 length, rem, _ := ReadLength(d) // We know we have enough bytes to read the length 266 buf.WriteByte('(') 267 buf.WriteString(strconv.Itoa(int(length))) 268 length -= 4 269 buf.WriteString("){") 270 var elem Element 271 var ok bool 272 for length > 1 { 273 elem, rem, ok = ReadElement(rem) 274 length -= int32(len(elem)) 275 if !ok { 276 buf.WriteString(fmt.Sprintf("<malformed (%d)>", length)) 277 break 278 } 279 fmt.Fprintf(&buf, "%s ", elem.DebugString()) 280 } 281 buf.WriteByte('}') 282 283 return buf.String() 284} 285 286// String outputs an ExtendedJSON version of Document. If the document is not valid, this method 287// returns an empty string. 288func (d Document) String() string { 289 if len(d) < 5 { 290 return "" 291 } 292 var buf bytes.Buffer 293 buf.WriteByte('{') 294 295 length, rem, _ := ReadLength(d) // We know we have enough bytes to read the length 296 297 length -= 4 298 299 var elem Element 300 var ok bool 301 first := true 302 for length > 1 { 303 if !first { 304 buf.WriteByte(',') 305 } 306 elem, rem, ok = ReadElement(rem) 307 length -= int32(len(elem)) 308 if !ok { 309 return "" 310 } 311 fmt.Fprintf(&buf, "%s", elem.String()) 312 first = false 313 } 314 buf.WriteByte('}') 315 316 return buf.String() 317} 318 319// Elements returns this document as a slice of elements. The returned slice will contain valid 320// elements. If the document is not valid, the elements up to the invalid point will be returned 321// along with an error. 322func (d Document) Elements() ([]Element, error) { 323 length, rem, ok := ReadLength(d) 324 if !ok { 325 return nil, NewInsufficientBytesError(d, rem) 326 } 327 328 length -= 4 329 330 var elem Element 331 var elems []Element 332 for length > 1 { 333 elem, rem, ok = ReadElement(rem) 334 length -= int32(len(elem)) 335 if !ok { 336 return elems, NewInsufficientBytesError(d, rem) 337 } 338 if err := elem.Validate(); err != nil { 339 return elems, err 340 } 341 elems = append(elems, elem) 342 } 343 return elems, nil 344} 345 346// Values returns this document as a slice of values. The returned slice will contain valid values. 347// If the document is not valid, the values up to the invalid point will be returned along with an 348// error. 349func (d Document) Values() ([]Value, error) { 350 return values(d) 351} 352 353func values(b []byte) ([]Value, error) { 354 length, rem, ok := ReadLength(b) 355 if !ok { 356 return nil, NewInsufficientBytesError(b, rem) 357 } 358 359 length -= 4 360 361 var elem Element 362 var vals []Value 363 for length > 1 { 364 elem, rem, ok = ReadElement(rem) 365 length -= int32(len(elem)) 366 if !ok { 367 return vals, NewInsufficientBytesError(b, rem) 368 } 369 if err := elem.Value().Validate(); err != nil { 370 return vals, err 371 } 372 vals = append(vals, elem.Value()) 373 } 374 return vals, nil 375} 376 377// Validate validates the document and ensures the elements contained within are valid. 378func (d Document) Validate() error { 379 length, rem, ok := ReadLength(d) 380 if !ok { 381 return NewInsufficientBytesError(d, rem) 382 } 383 if int(length) > len(d) { 384 return NewDocumentLengthError(int(length), len(d)) 385 } 386 if d[length-1] != 0x00 { 387 return ErrMissingNull 388 } 389 390 length -= 4 391 var elem Element 392 393 for length > 1 { 394 elem, rem, ok = ReadElement(rem) 395 length -= int32(len(elem)) 396 if !ok { 397 return NewInsufficientBytesError(d, rem) 398 } 399 err := elem.Validate() 400 if err != nil { 401 return err 402 } 403 } 404 405 if len(rem) < 1 || rem[0] != 0x00 { 406 return ErrMissingNull 407 } 408 return nil 409} 410