@@ -143,73 +143,69 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
143143 . check_rate_limit ( auth. user ( ) . id , rate_limit_action, & mut conn)
144144 . await ?;
145145
146- spawn_blocking ( move || {
147- use diesel:: RunQueryDsl ;
148-
149- let conn: & mut AsyncConnectionWrapper < _ > = & mut conn. into ( ) ;
150-
151- let api_token_id = auth. api_token_id ( ) ;
152- let user = auth. user ( ) ;
153-
154- let content_length = tarball_bytes. len ( ) as u64 ;
155-
156- let maximums = Maximums :: new (
157- existing_crate. as_ref ( ) . and_then ( |c| c. max_upload_size ) ,
158- app. config . max_upload_size ,
159- app. config . max_unpack_size ,
160- ) ;
161-
162- if content_length > maximums. max_upload_size {
163- return Err ( custom ( StatusCode :: PAYLOAD_TOO_LARGE , format ! (
164- "max upload size is: {}" ,
165- maximums. max_upload_size
166- ) ) ) ;
167- }
146+ let content_length = tarball_bytes. len ( ) as u64 ;
147+
148+ let maximums = Maximums :: new (
149+ existing_crate. as_ref ( ) . and_then ( |c| c. max_upload_size ) ,
150+ app. config . max_upload_size ,
151+ app. config . max_unpack_size ,
152+ ) ;
153+
154+ if content_length > maximums. max_upload_size {
155+ return Err ( custom (
156+ StatusCode :: PAYLOAD_TOO_LARGE ,
157+ format ! ( "max upload size is: {}" , maximums. max_upload_size) ,
158+ ) ) ;
159+ }
168160
161+ let tarball_info = spawn_blocking ( {
169162 let pkg_name = format ! ( "{}-{}" , & * metadata. name, & version_string) ;
170- let tarball_info = process_tarball ( & pkg_name, & * tarball_bytes, maximums. max_unpack_size ) ?;
171-
172- // `unwrap()` is safe here since `process_tarball()` validates that
173- // we only accept manifests with a `package` section and without
174- // inheritance.
175- let package = tarball_info. manifest . package . unwrap ( ) ;
176-
177- let description = package. description . map ( |it| it. as_local ( ) . unwrap ( ) ) ;
178- let mut license = package. license . map ( |it| it. as_local ( ) . unwrap ( ) ) ;
179- let license_file = package. license_file . map ( |it| it. as_local ( ) . unwrap ( ) ) ;
180- let homepage = package. homepage . map ( |it| it. as_local ( ) . unwrap ( ) ) ;
181- let documentation = package. documentation . map ( |it| it. as_local ( ) . unwrap ( ) ) ;
182- let repository = package. repository . map ( |it| it. as_local ( ) . unwrap ( ) ) ;
183- let rust_version = package. rust_version . map ( |rv| rv. as_local ( ) . unwrap ( ) ) ;
184- let edition = package. edition . map ( |rv| rv. as_local ( ) . unwrap ( ) ) ;
185-
186- // Make sure required fields are provided
187- fn empty ( s : Option < & String > ) -> bool {
188- s. map_or ( true , String :: is_empty)
189- }
163+ let tarball_bytes = tarball_bytes. clone ( ) ;
164+ move || process_tarball ( & pkg_name, & * tarball_bytes, maximums. max_unpack_size )
165+ } )
166+ . await ??;
167+
168+ // `unwrap()` is safe here since `process_tarball()` validates that
169+ // we only accept manifests with a `package` section and without
170+ // inheritance.
171+ let package = tarball_info. manifest . package . unwrap ( ) ;
172+
173+ let description = package. description . map ( |it| it. as_local ( ) . unwrap ( ) ) ;
174+ let mut license = package. license . map ( |it| it. as_local ( ) . unwrap ( ) ) ;
175+ let license_file = package. license_file . map ( |it| it. as_local ( ) . unwrap ( ) ) ;
176+ let homepage = package. homepage . map ( |it| it. as_local ( ) . unwrap ( ) ) ;
177+ let documentation = package. documentation . map ( |it| it. as_local ( ) . unwrap ( ) ) ;
178+ let repository = package. repository . map ( |it| it. as_local ( ) . unwrap ( ) ) ;
179+ let rust_version = package. rust_version . map ( |rv| rv. as_local ( ) . unwrap ( ) ) ;
180+ let edition = package. edition . map ( |rv| rv. as_local ( ) . unwrap ( ) ) ;
181+
182+ // Make sure required fields are provided
183+ fn empty ( s : Option < & String > ) -> bool {
184+ s. map_or ( true , String :: is_empty)
185+ }
190186
191- // It can have up to three elements per below conditions.
192- let mut missing = Vec :: with_capacity ( 3 ) ;
193- if empty ( description. as_ref ( ) ) {
194- missing. push ( "description" ) ;
195- }
196- if empty ( license. as_ref ( ) ) && empty ( license_file. as_ref ( ) ) {
197- missing. push ( "license" ) ;
198- }
199- if !missing. is_empty ( ) {
200- let message = missing_metadata_error_message ( & missing) ;
201- return Err ( bad_request ( & message) ) ;
202- }
187+ // It can have up to three elements per below conditions.
188+ let mut missing = Vec :: with_capacity ( 3 ) ;
189+ if empty ( description. as_ref ( ) ) {
190+ missing. push ( "description" ) ;
191+ }
192+ if empty ( license. as_ref ( ) ) && empty ( license_file. as_ref ( ) ) {
193+ missing. push ( "license" ) ;
194+ }
195+ if !missing. is_empty ( ) {
196+ let message = missing_metadata_error_message ( & missing) ;
197+ return Err ( bad_request ( & message) ) ;
198+ }
203199
204- if let Some ( description) = & description {
205- if description. len ( ) > MAX_DESCRIPTION_LENGTH {
206- return Err ( bad_request ( format ! ( "The `description` is too long. A maximum of {MAX_DESCRIPTION_LENGTH} characters are currently allowed." ) ) ) ;
207- }
200+ if let Some ( description) = & description {
201+ if description. len ( ) > MAX_DESCRIPTION_LENGTH {
202+ return Err ( bad_request ( format ! ( "The `description` is too long. A maximum of {MAX_DESCRIPTION_LENGTH} characters are currently allowed." ) ) ) ;
208203 }
204+ }
209205
210- if let Some ( ref license) = license {
211- parse_license_expr ( license) . map_err ( |e| bad_request ( format_args ! (
212- "unknown or invalid license expression; \
206+ if let Some ( ref license) = license {
207+ parse_license_expr ( license) . map_err ( |e| bad_request ( format_args ! (
208+ "unknown or invalid license expression; \
213209 see http://opensource.org/licenses for options, \
214210 and http://spdx.org/licenses/ for their identifiers\n \
215211 Note: If you have a non-standard license that is not listed by SPDX, \
@@ -218,75 +214,76 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
218214 See https://doc.rust-lang.org/cargo/reference/manifest.html#the-license-and-license-file-fields \
219215 for more information.\n \
220216 {e}"
221- ) ) ) ?;
222- } else if license_file. is_some ( ) {
223- // If no license is given, but a license file is given, flag this
224- // crate as having a nonstandard license. Note that we don't
225- // actually do anything else with license_file currently.
226- license = Some ( String :: from ( "non-standard" ) ) ;
227- }
217+ ) ) ) ?;
218+ } else if license_file. is_some ( ) {
219+ // If no license is given, but a license file is given, flag this
220+ // crate as having a nonstandard license. Note that we don't
221+ // actually do anything else with license_file currently.
222+ license = Some ( String :: from ( "non-standard" ) ) ;
223+ }
228224
229- validate_url ( homepage. as_deref ( ) , "homepage" ) ?;
230- validate_url ( documentation. as_deref ( ) , "documentation" ) ?;
231- validate_url ( repository. as_deref ( ) , "repository" ) ?;
232- if let Some ( ref rust_version) = rust_version {
233- validate_rust_version ( rust_version) ?;
234- }
225+ validate_url ( homepage. as_deref ( ) , "homepage" ) ?;
226+ validate_url ( documentation. as_deref ( ) , "documentation" ) ?;
227+ validate_url ( repository. as_deref ( ) , "repository" ) ?;
228+ if let Some ( ref rust_version) = rust_version {
229+ validate_rust_version ( rust_version) ?;
230+ }
235231
236- let keywords = package
237- . keywords
238- . map ( |it| it. as_local ( ) . unwrap ( ) )
239- . unwrap_or_default ( ) ;
232+ let keywords = package
233+ . keywords
234+ . map ( |it| it. as_local ( ) . unwrap ( ) )
235+ . unwrap_or_default ( ) ;
240236
241- if keywords. len ( ) > 5 {
242- return Err ( bad_request ( "expected at most 5 keywords per crate" ) ) ;
243- }
237+ if keywords. len ( ) > 5 {
238+ return Err ( bad_request ( "expected at most 5 keywords per crate" ) ) ;
239+ }
244240
245- for keyword in keywords. iter ( ) {
246- if keyword. len ( ) > 20 {
247- return Err ( bad_request ( format ! (
248- "\" {keyword}\" is an invalid keyword (keywords must have less than 20 characters)"
249- ) ) ) ;
250- } else if !Keyword :: valid_name ( keyword) {
251- return Err ( bad_request ( format ! ( "\" {keyword}\" is an invalid keyword" ) ) ) ;
252- }
241+ for keyword in keywords. iter ( ) {
242+ if keyword. len ( ) > 20 {
243+ return Err ( bad_request ( format ! (
244+ "\" {keyword}\" is an invalid keyword (keywords must have less than 20 characters)"
245+ ) ) ) ;
246+ } else if !Keyword :: valid_name ( keyword) {
247+ return Err ( bad_request ( format ! ( "\" {keyword}\" is an invalid keyword" ) ) ) ;
253248 }
249+ }
254250
255- let categories = package
256- . categories
257- . map ( |it| it. as_local ( ) . unwrap ( ) )
258- . unwrap_or_default ( ) ;
251+ let categories = package
252+ . categories
253+ . map ( |it| it. as_local ( ) . unwrap ( ) )
254+ . unwrap_or_default ( ) ;
259255
260- if categories. len ( ) > 5 {
261- return Err ( bad_request ( "expected at most 5 categories per crate" ) ) ;
262- }
256+ if categories. len ( ) > 5 {
257+ return Err ( bad_request ( "expected at most 5 categories per crate" ) ) ;
258+ }
263259
264- let max_features = existing_crate. as_ref ( )
265- . and_then ( |c| c. max_features . map ( |mf| mf as usize ) )
266- . unwrap_or ( app. config . max_features ) ;
260+ let max_features = existing_crate
261+ . as_ref ( )
262+ . and_then ( |c| c. max_features . map ( |mf| mf as usize ) )
263+ . unwrap_or ( app. config . max_features ) ;
267264
268- let features = tarball_info. manifest . features . unwrap_or_default ( ) ;
269- let num_features = features. len ( ) ;
270- if num_features > max_features {
271- return Err ( bad_request ( format ! (
272- "crates.io only allows a maximum number of {max_features} \
265+ let features = tarball_info. manifest . features . unwrap_or_default ( ) ;
266+ let num_features = features. len ( ) ;
267+ if num_features > max_features {
268+ return Err ( bad_request ( format ! (
269+ "crates.io only allows a maximum number of {max_features} \
273270 features, but your crate is declaring {num_features} features.\n \
274271 \n \
275272 Take a look at https://blog.rust-lang.org/2023/10/26/broken-badges-and-23k-keywords.html \
276273 to understand why this restriction was introduced.\n \
277274 \n \
278275 If you have a use case that requires an increase of this limit, \
279276 please send us an email to [email protected] to discuss the details."280- ) ) ) ;
281- }
277+ ) ) ) ;
278+ }
282279
283- for ( key, values) in features. iter ( ) {
284- Crate :: validate_feature_name ( key) . map_err ( bad_request) ?;
280+ for ( key, values) in features. iter ( ) {
281+ Crate :: validate_feature_name ( key) . map_err ( bad_request) ?;
285282
286- let num_features = values. len ( ) ;
287- if num_features > max_features {
288- return Err ( bad_request ( format ! (
289- "crates.io only allows a maximum number of {max_features} \
283+ let num_features = values. len ( ) ;
284+ if num_features > max_features {
285+ return Err ( bad_request ( format ! (
286+ "crates.io only allows a maximum number of {max_features} \
290287 features or dependencies that another feature can enable, \
291288 but the \" {key}\" feature of your crate is enabling \
292289 {num_features} features or dependencies.\n \
@@ -296,34 +293,42 @@ pub async fn publish(app: AppState, req: BytesRequest) -> AppResult<Json<GoodCra
296293 \n \
297294 If you have a use case that requires an increase of this limit, \
298295 please send us an email to [email protected] to discuss the details."299- ) ) ) ;
300- }
296+ ) ) ) ;
297+ }
301298
302- for value in values. iter ( ) {
303- Crate :: validate_feature ( value) . map_err ( bad_request) ?;
304- }
299+ for value in values. iter ( ) {
300+ Crate :: validate_feature ( value) . map_err ( bad_request) ?;
305301 }
302+ }
306303
307- let deps = convert_dependencies (
308- tarball_info. manifest . dependencies . as_ref ( ) ,
309- tarball_info. manifest . dev_dependencies . as_ref ( ) ,
310- tarball_info. manifest . build_dependencies . as_ref ( ) ,
311- tarball_info. manifest . target . as_ref ( )
312- ) ;
304+ let deps = convert_dependencies (
305+ tarball_info. manifest . dependencies . as_ref ( ) ,
306+ tarball_info. manifest . dev_dependencies . as_ref ( ) ,
307+ tarball_info. manifest . build_dependencies . as_ref ( ) ,
308+ tarball_info. manifest . target . as_ref ( ) ,
309+ ) ;
313310
314- let max_dependencies = app. config . max_dependencies ;
315- if deps. len ( ) > max_dependencies {
316- return Err ( bad_request ( format ! (
317- "crates.io only allows a maximum number of {max_dependencies} dependencies.\n \
311+ let max_dependencies = app. config . max_dependencies ;
312+ if deps. len ( ) > max_dependencies {
313+ return Err ( bad_request ( format ! (
314+ "crates.io only allows a maximum number of {max_dependencies} dependencies.\n \
318315 \n \
319316 If you have a use case that requires an increase of this limit, \
320317 please send us an email to [email protected] to discuss the details."321- ) ) ) ;
322- }
318+ ) ) ) ;
319+ }
323320
324- for dep in & deps {
325- validate_dependency ( dep) ?;
326- }
321+ for dep in & deps {
322+ validate_dependency ( dep) ?;
323+ }
324+
325+ spawn_blocking ( move || {
326+ use diesel:: RunQueryDsl ;
327+
328+ let conn: & mut AsyncConnectionWrapper < _ > = & mut conn. into ( ) ;
329+
330+ let api_token_id = auth. api_token_id ( ) ;
331+ let user = auth. user ( ) ;
327332
328333 // Create a transaction on the database, if there are no errors,
329334 // commit the transactions to record a new or updated crate.
0 commit comments